summaryrefslogtreecommitdiff
path: root/debian/patches
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches')
-rw-r--r--debian/patches/authorized-keys-man-symlink.patch26
-rw-r--r--debian/patches/conch-old-privkey-format.patch68
-rw-r--r--debian/patches/debian-banner.patch163
-rw-r--r--debian/patches/debian-config.patch241
-rw-r--r--debian/patches/dnssec-sshfp.patch94
-rw-r--r--debian/patches/doc-hash-tab-completion.patch28
-rw-r--r--debian/patches/gnome-ssh-askpass2-icon.patch26
-rw-r--r--debian/patches/gssapi.patch3983
-rw-r--r--debian/patches/keepalive-extensions.patch135
-rw-r--r--debian/patches/mention-ssh-keygen-on-keychange.patch44
-rw-r--r--debian/patches/no-openssl-version-status.patch62
-rw-r--r--debian/patches/openbsd-docs.patch148
-rw-r--r--debian/patches/package-versioning.patch47
-rw-r--r--debian/patches/restore-authorized_keys2.patch35
-rw-r--r--debian/patches/restore-tcp-wrappers.patch172
-rw-r--r--debian/patches/revert-ipqos-defaults.patch93
-rw-r--r--debian/patches/scp-quoting.patch41
-rw-r--r--debian/patches/selinux-role.patch472
-rw-r--r--debian/patches/series25
-rw-r--r--debian/patches/shell-path.patch39
-rw-r--r--debian/patches/ssh-agent-setgid.patch40
-rw-r--r--debian/patches/ssh-argv0.patch31
-rw-r--r--debian/patches/ssh-vulnkey-compat.patch42
-rw-r--r--debian/patches/syslog-level-silent.patch47
-rw-r--r--debian/patches/systemd-readiness.patch84
-rw-r--r--debian/patches/user-group-modes.patch210
26 files changed, 6396 insertions, 0 deletions
diff --git a/debian/patches/authorized-keys-man-symlink.patch b/debian/patches/authorized-keys-man-symlink.patch
new file mode 100644
index 000000000..43a160a0f
--- /dev/null
+++ b/debian/patches/authorized-keys-man-symlink.patch
@@ -0,0 +1,26 @@
1From b0cb3badf4d423f8ea7bf950e55ca72878cc224b Mon Sep 17 00:00:00 2001
2From: Tomas Pospisek <tpo_deb@sourcepole.ch>
3Date: Sun, 9 Feb 2014 16:10:07 +0000
4Subject: Install authorized_keys(5) as a symlink to sshd(8)
5
6Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1720
7Bug-Debian: http://bugs.debian.org/441817
8Last-Update: 2013-09-14
9
10Patch-Name: authorized-keys-man-symlink.patch
11---
12 Makefile.in | 1 +
13 1 file changed, 1 insertion(+)
14
15diff --git a/Makefile.in b/Makefile.in
16index b68c1710f..bff1db49b 100644
17--- a/Makefile.in
18+++ b/Makefile.in
19@@ -402,6 +402,7 @@ install-files:
20 $(INSTALL) -m 644 sshd_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/sshd_config.5
21 $(INSTALL) -m 644 ssh_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh_config.5
22 $(INSTALL) -m 644 sshd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8
23+ ln -s ../$(mansubdir)8/sshd.8 $(DESTDIR)$(mandir)/$(mansubdir)5/authorized_keys.5
24 $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1
25 $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8
26 $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8
diff --git a/debian/patches/conch-old-privkey-format.patch b/debian/patches/conch-old-privkey-format.patch
new file mode 100644
index 000000000..b04c21060
--- /dev/null
+++ b/debian/patches/conch-old-privkey-format.patch
@@ -0,0 +1,68 @@
1From 311da721c2a5c6d147738e0699fa49d04cd5762a Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Thu, 30 Aug 2018 00:58:56 +0100
4Subject: Work around conch interoperability failure
5
6Twisted Conch fails to read private keys in the new format
7(https://twistedmatrix.com/trac/ticket/9515). Work around this until it
8can be fixed in Twisted.
9
10Forwarded: not-needed
11Last-Update: 2019-10-09
12
13Patch-Name: conch-old-privkey-format.patch
14---
15 regress/Makefile | 2 +-
16 regress/conch-ciphers.sh | 2 +-
17 regress/test-exec.sh | 12 ++++++++++++
18 3 files changed, 14 insertions(+), 2 deletions(-)
19
20diff --git a/regress/Makefile b/regress/Makefile
21index 774c10d41..01e257a94 100644
22--- a/regress/Makefile
23+++ b/regress/Makefile
24@@ -120,7 +120,7 @@ CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \
25 rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \
26 scp-ssh-wrapper.scp setuid-allowed sftp-server.log \
27 sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \
28- ssh-rsa_oldfmt \
29+ ssh-rsa_oldfmt ssh-rsa_oldfmt.pub \
30 ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \
31 ssh_proxy_envpass sshd.log sshd_config sshd_config_minimal \
32 sshd_config.* sshd_proxy sshd_proxy.* sshd_proxy_bak \
33diff --git a/regress/conch-ciphers.sh b/regress/conch-ciphers.sh
34index 6678813a2..6ff5da20b 100644
35--- a/regress/conch-ciphers.sh
36+++ b/regress/conch-ciphers.sh
37@@ -16,7 +16,7 @@ for c in aes256-ctr aes256-cbc aes192-ctr aes192-cbc aes128-ctr aes128-cbc \
38 rm -f ${COPY}
39 # XXX the 2nd "cat" seems to be needed because of buggy FD handling
40 # in conch
41- ${CONCH} --identity $OBJ/ssh-rsa --port $PORT --user $USER -e none \
42+ ${CONCH} --identity $OBJ/ssh-rsa_oldfmt --port $PORT --user $USER -e none \
43 --known-hosts $OBJ/known_hosts --notty --noagent --nox11 -n \
44 127.0.0.1 "cat ${DATA}" 2>/dev/null | cat > ${COPY}
45 if [ $? -ne 0 ]; then
46diff --git a/regress/test-exec.sh b/regress/test-exec.sh
47index f5e3ee6f5..a3a40719f 100644
48--- a/regress/test-exec.sh
49+++ b/regress/test-exec.sh
50@@ -573,6 +573,18 @@ REGRESS_INTEROP_CONCH=no
51 if test -x "$CONCH" ; then
52 REGRESS_INTEROP_CONCH=yes
53 fi
54+case "$SCRIPT" in
55+*conch*) ;;
56+*) REGRESS_INTEROP_CONCH=no
57+esac
58+
59+if test "$REGRESS_INTEROP_CONCH" = "yes" ; then
60+ # Convert rsa key to old format to work around
61+ # https://twistedmatrix.com/trac/ticket/9515
62+ cp $OBJ/ssh-rsa $OBJ/ssh-rsa_oldfmt
63+ cp $OBJ/ssh-rsa.pub $OBJ/ssh-rsa_oldfmt.pub
64+ ${SSHKEYGEN} -p -N '' -m PEM -f $OBJ/ssh-rsa_oldfmt >/dev/null
65+fi
66
67 # If PuTTY is present and we are running a PuTTY test, prepare keys and
68 # configuration
diff --git a/debian/patches/debian-banner.patch b/debian/patches/debian-banner.patch
new file mode 100644
index 000000000..0d998fdd4
--- /dev/null
+++ b/debian/patches/debian-banner.patch
@@ -0,0 +1,163 @@
1From 7d20d00ea24ec0c3fffacc80ab271d0699d198c6 Mon Sep 17 00:00:00 2001
2From: Kees Cook <kees@debian.org>
3Date: Sun, 9 Feb 2014 16:10:06 +0000
4Subject: Add DebianBanner server configuration option
5
6Setting this to "no" causes sshd to omit the Debian revision from its
7initial protocol handshake, for those scared by package-versioning.patch.
8
9Bug-Debian: http://bugs.debian.org/562048
10Forwarded: not-needed
11Last-Update: 2020-02-21
12
13Patch-Name: debian-banner.patch
14---
15 kex.c | 5 +++--
16 kex.h | 2 +-
17 servconf.c | 9 +++++++++
18 servconf.h | 2 ++
19 sshconnect.c | 2 +-
20 sshd.c | 3 ++-
21 sshd_config.5 | 5 +++++
22 7 files changed, 23 insertions(+), 5 deletions(-)
23
24diff --git a/kex.c b/kex.c
25index f638942d3..2abfbb95a 100644
26--- a/kex.c
27+++ b/kex.c
28@@ -1226,7 +1226,7 @@ send_error(struct ssh *ssh, char *msg)
29 */
30 int
31 kex_exchange_identification(struct ssh *ssh, int timeout_ms,
32- const char *version_addendum)
33+ int debian_banner, const char *version_addendum)
34 {
35 int remote_major, remote_minor, mismatch;
36 size_t len, i, n;
37@@ -1244,7 +1244,8 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
38 if (version_addendum != NULL && *version_addendum == '\0')
39 version_addendum = NULL;
40 if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
41- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_RELEASE,
42+ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2,
43+ debian_banner ? SSH_RELEASE : SSH_RELEASE_MINIMUM,
44 version_addendum == NULL ? "" : " ",
45 version_addendum == NULL ? "" : version_addendum)) != 0) {
46 error("%s: sshbuf_putf: %s", __func__, ssh_err(r));
47diff --git a/kex.h b/kex.h
48index fe7141414..938dca03b 100644
49--- a/kex.h
50+++ b/kex.h
51@@ -194,7 +194,7 @@ char *kex_names_cat(const char *, const char *);
52 int kex_assemble_names(char **, const char *, const char *);
53 int kex_gss_names_valid(const char *);
54
55-int kex_exchange_identification(struct ssh *, int, const char *);
56+int kex_exchange_identification(struct ssh *, int, int, const char *);
57
58 struct kex *kex_new(void);
59 int kex_ready(struct ssh *, char *[PROPOSAL_MAX]);
60diff --git a/servconf.c b/servconf.c
61index bf3cd84a4..7bbc25c2e 100644
62--- a/servconf.c
63+++ b/servconf.c
64@@ -194,6 +194,7 @@ initialize_server_options(ServerOptions *options)
65 options->fingerprint_hash = -1;
66 options->disable_forwarding = -1;
67 options->expose_userauth_info = -1;
68+ options->debian_banner = -1;
69 }
70
71 /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
72@@ -468,6 +469,8 @@ fill_default_server_options(ServerOptions *options)
73 options->expose_userauth_info = 0;
74 if (options->sk_provider == NULL)
75 options->sk_provider = xstrdup("internal");
76+ if (options->debian_banner == -1)
77+ options->debian_banner = 1;
78
79 assemble_algorithms(options);
80
81@@ -556,6 +559,7 @@ typedef enum {
82 sStreamLocalBindMask, sStreamLocalBindUnlink,
83 sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
84 sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
85+ sDebianBanner,
86 sDeprecated, sIgnore, sUnsupported
87 } ServerOpCodes;
88
89@@ -719,6 +723,7 @@ static struct {
90 { "rdomain", sRDomain, SSHCFG_ALL },
91 { "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
92 { "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
93+ { "debianbanner", sDebianBanner, SSHCFG_GLOBAL },
94 { NULL, sBadOption, 0 }
95 };
96
97@@ -2382,6 +2387,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
98 *charptr = xstrdup(arg);
99 break;
100
101+ case sDebianBanner:
102+ intptr = &options->debian_banner;
103+ goto parse_flag;
104+
105 case sDeprecated:
106 case sIgnore:
107 case sUnsupported:
108diff --git a/servconf.h b/servconf.h
109index 3f47ea25e..3fa05fcac 100644
110--- a/servconf.h
111+++ b/servconf.h
112@@ -221,6 +221,8 @@ typedef struct {
113 int expose_userauth_info;
114 u_int64_t timing_secret;
115 char *sk_provider;
116+
117+ int debian_banner;
118 } ServerOptions;
119
120 /* Information about the incoming connection as used by Match */
121diff --git a/sshconnect.c b/sshconnect.c
122index b796d3c8a..9f2412e0d 100644
123--- a/sshconnect.c
124+++ b/sshconnect.c
125@@ -1292,7 +1292,7 @@ ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost,
126 lowercase(host);
127
128 /* Exchange protocol version identification strings with the server. */
129- if (kex_exchange_identification(ssh, timeout_ms, NULL) != 0)
130+ if (kex_exchange_identification(ssh, timeout_ms, 1, NULL) != 0)
131 cleanup_exit(255); /* error already logged */
132
133 /* Put the connection into non-blocking mode. */
134diff --git a/sshd.c b/sshd.c
135index 65916fc6d..da876a900 100644
136--- a/sshd.c
137+++ b/sshd.c
138@@ -2187,7 +2187,8 @@ main(int ac, char **av)
139 if (!debug_flag)
140 alarm(options.login_grace_time);
141
142- if (kex_exchange_identification(ssh, -1, options.version_addendum) != 0)
143+ if (kex_exchange_identification(ssh, -1, options.debian_banner,
144+ options.version_addendum) != 0)
145 cleanup_exit(255); /* error already logged */
146
147 ssh_packet_set_nonblocking(ssh);
148diff --git a/sshd_config.5 b/sshd_config.5
149index ebd09f891..c926f584c 100644
150--- a/sshd_config.5
151+++ b/sshd_config.5
152@@ -542,6 +542,11 @@ or
153 .Cm no .
154 The default is
155 .Cm yes .
156+.It Cm DebianBanner
157+Specifies whether the distribution-specified extra version suffix is
158+included during initial protocol handshake.
159+The default is
160+.Cm yes .
161 .It Cm DenyGroups
162 This keyword can be followed by a list of group name patterns, separated
163 by spaces.
diff --git a/debian/patches/debian-config.patch b/debian/patches/debian-config.patch
new file mode 100644
index 000000000..e5c690915
--- /dev/null
+++ b/debian/patches/debian-config.patch
@@ -0,0 +1,241 @@
1From cc80ecc65d57a9e68ce84d67bcfece281ffa0e9f Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 9 Feb 2014 16:10:18 +0000
4Subject: Various Debian-specific configuration changes
5
6ssh: Enable ForwardX11Trusted, returning to earlier semantics which cause
7fewer problems with existing setups (http://bugs.debian.org/237021).
8
9ssh: Set 'SendEnv LANG LC_*' by default (http://bugs.debian.org/264024).
10
11ssh: Enable HashKnownHosts by default to try to limit the spread of ssh
12worms.
13
14ssh: Enable GSSAPIAuthentication by default.
15
16sshd: Enable PAM, disable ChallengeResponseAuthentication, and disable
17PrintMotd.
18
19sshd: Enable X11Forwarding.
20
21sshd: Set 'AcceptEnv LANG LC_*' by default.
22
23sshd: Change sftp subsystem path to /usr/lib/openssh/sftp-server.
24
25Document all of this.
26
27Author: Russ Allbery <rra@debian.org>
28Forwarded: not-needed
29Last-Update: 2020-02-19
30
31Patch-Name: debian-config.patch
32---
33 readconf.c | 2 +-
34 ssh.1 | 24 ++++++++++++++++++++++++
35 ssh_config | 6 +++++-
36 ssh_config.5 | 19 ++++++++++++++++++-
37 sshd_config | 16 ++++++++++------
38 sshd_config.5 | 22 ++++++++++++++++++++++
39 6 files changed, 80 insertions(+), 9 deletions(-)
40
41diff --git a/readconf.c b/readconf.c
42index 7f251dd4a..e82024678 100644
43--- a/readconf.c
44+++ b/readconf.c
45@@ -2087,7 +2087,7 @@ fill_default_options(Options * options)
46 if (options->forward_x11 == -1)
47 options->forward_x11 = 0;
48 if (options->forward_x11_trusted == -1)
49- options->forward_x11_trusted = 0;
50+ options->forward_x11_trusted = 1;
51 if (options->forward_x11_timeout == -1)
52 options->forward_x11_timeout = 1200;
53 /*
54diff --git a/ssh.1 b/ssh.1
55index b33a8049f..a8967c2f8 100644
56--- a/ssh.1
57+++ b/ssh.1
58@@ -809,6 +809,16 @@ directive in
59 .Xr ssh_config 5
60 for more information.
61 .Pp
62+(Debian-specific: X11 forwarding is not subjected to X11 SECURITY extension
63+restrictions by default, because too many programs currently crash in this
64+mode.
65+Set the
66+.Cm ForwardX11Trusted
67+option to
68+.Dq no
69+to restore the upstream behaviour.
70+This may change in future depending on client-side improvements.)
71+.Pp
72 .It Fl x
73 Disables X11 forwarding.
74 .Pp
75@@ -817,6 +827,20 @@ Enables trusted X11 forwarding.
76 Trusted X11 forwardings are not subjected to the X11 SECURITY extension
77 controls.
78 .Pp
79+(Debian-specific: In the default configuration, this option is equivalent to
80+.Fl X ,
81+since
82+.Cm ForwardX11Trusted
83+defaults to
84+.Dq yes
85+as described above.
86+Set the
87+.Cm ForwardX11Trusted
88+option to
89+.Dq no
90+to restore the upstream behaviour.
91+This may change in future depending on client-side improvements.)
92+.Pp
93 .It Fl y
94 Send log information using the
95 .Xr syslog 3
96diff --git a/ssh_config b/ssh_config
97index 1ff999b68..6dd6ecf87 100644
98--- a/ssh_config
99+++ b/ssh_config
100@@ -17,9 +17,10 @@
101 # list of available options, their meanings and defaults, please see the
102 # ssh_config(5) man page.
103
104-# Host *
105+Host *
106 # ForwardAgent no
107 # ForwardX11 no
108+# ForwardX11Trusted yes
109 # PasswordAuthentication yes
110 # HostbasedAuthentication no
111 # GSSAPIAuthentication no
112@@ -45,3 +46,6 @@
113 # VisualHostKey no
114 # ProxyCommand ssh -q -W %h:%p gateway.example.com
115 # RekeyLimit 1G 1h
116+ SendEnv LANG LC_*
117+ HashKnownHosts yes
118+ GSSAPIAuthentication yes
119diff --git a/ssh_config.5 b/ssh_config.5
120index c6eaa63e7..5c90d3e02 100644
121--- a/ssh_config.5
122+++ b/ssh_config.5
123@@ -71,6 +71,22 @@ Since the first obtained value for each parameter is used, more
124 host-specific declarations should be given near the beginning of the
125 file, and general defaults at the end.
126 .Pp
127+Note that the Debian
128+.Ic openssh-client
129+package sets several options as standard in
130+.Pa /etc/ssh/ssh_config
131+which are not the default in
132+.Xr ssh 1 :
133+.Pp
134+.Bl -bullet -offset indent -compact
135+.It
136+.Cm SendEnv No LANG LC_*
137+.It
138+.Cm HashKnownHosts No yes
139+.It
140+.Cm GSSAPIAuthentication No yes
141+.El
142+.Pp
143 The file contains keyword-argument pairs, one per line.
144 Lines starting with
145 .Ql #
146@@ -729,11 +745,12 @@ elapsed.
147 .It Cm ForwardX11Trusted
148 If this option is set to
149 .Cm yes ,
150+(the Debian-specific default),
151 remote X11 clients will have full access to the original X11 display.
152 .Pp
153 If this option is set to
154 .Cm no
155-(the default),
156+(the upstream default),
157 remote X11 clients will be considered untrusted and prevented
158 from stealing or tampering with data belonging to trusted X11
159 clients.
160diff --git a/sshd_config b/sshd_config
161index 2c48105f8..ed8272f6d 100644
162--- a/sshd_config
163+++ b/sshd_config
164@@ -57,8 +57,9 @@ AuthorizedKeysFile .ssh/authorized_keys
165 #PasswordAuthentication yes
166 #PermitEmptyPasswords no
167
168-# Change to no to disable s/key passwords
169-#ChallengeResponseAuthentication yes
170+# Change to yes to enable challenge-response passwords (beware issues with
171+# some PAM modules and threads)
172+ChallengeResponseAuthentication no
173
174 # Kerberos options
175 #KerberosAuthentication no
176@@ -81,16 +82,16 @@ AuthorizedKeysFile .ssh/authorized_keys
177 # If you just want the PAM account and session checks to run without
178 # PAM authentication, then enable this but set PasswordAuthentication
179 # and ChallengeResponseAuthentication to 'no'.
180-#UsePAM no
181+UsePAM yes
182
183 #AllowAgentForwarding yes
184 #AllowTcpForwarding yes
185 #GatewayPorts no
186-#X11Forwarding no
187+X11Forwarding yes
188 #X11DisplayOffset 10
189 #X11UseLocalhost yes
190 #PermitTTY yes
191-#PrintMotd yes
192+PrintMotd no
193 #PrintLastLog yes
194 #TCPKeepAlive yes
195 #PermitUserEnvironment no
196@@ -107,8 +108,11 @@ AuthorizedKeysFile .ssh/authorized_keys
197 # no default banner path
198 #Banner none
199
200+# Allow client to pass locale environment variables
201+AcceptEnv LANG LC_*
202+
203 # override default of no subsystems
204-Subsystem sftp /usr/libexec/sftp-server
205+Subsystem sftp /usr/lib/openssh/sftp-server
206
207 # Example of overriding settings on a per-user basis
208 #Match User anoncvs
209diff --git a/sshd_config.5 b/sshd_config.5
210index 25f4b8117..b8bea2ad7 100644
211--- a/sshd_config.5
212+++ b/sshd_config.5
213@@ -56,6 +56,28 @@ Arguments may optionally be enclosed in double quotes
214 .Pq \&"
215 in order to represent arguments containing spaces.
216 .Pp
217+Note that the Debian
218+.Ic openssh-server
219+package sets several options as standard in
220+.Pa /etc/ssh/sshd_config
221+which are not the default in
222+.Xr sshd 8 :
223+.Pp
224+.Bl -bullet -offset indent -compact
225+.It
226+.Cm ChallengeResponseAuthentication No no
227+.It
228+.Cm X11Forwarding No yes
229+.It
230+.Cm PrintMotd No no
231+.It
232+.Cm AcceptEnv No LANG LC_*
233+.It
234+.Cm Subsystem No sftp /usr/lib/openssh/sftp-server
235+.It
236+.Cm UsePAM No yes
237+.El
238+.Pp
239 The possible
240 keywords and their meanings are as follows (note that
241 keywords are case-insensitive and arguments are case-sensitive):
diff --git a/debian/patches/dnssec-sshfp.patch b/debian/patches/dnssec-sshfp.patch
new file mode 100644
index 000000000..3744218ff
--- /dev/null
+++ b/debian/patches/dnssec-sshfp.patch
@@ -0,0 +1,94 @@
1From 74c1c0ef7689ea68dc8263f73c00ff8675f9f0fe Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 9 Feb 2014 16:10:01 +0000
4Subject: Force use of DNSSEC even if "options edns0" isn't in resolv.conf
5
6This allows SSHFP DNS records to be verified if glibc 2.11 is installed.
7
8Origin: vendor, https://cvs.fedoraproject.org/viewvc/F-12/openssh/openssh-5.2p1-edns.patch?revision=1.1&view=markup
9Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572049
10Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572049
11Last-Update: 2010-04-06
12
13Patch-Name: dnssec-sshfp.patch
14---
15 dns.c | 14 +++++++++++++-
16 openbsd-compat/getrrsetbyname.c | 10 +++++-----
17 openbsd-compat/getrrsetbyname.h | 3 +++
18 3 files changed, 21 insertions(+), 6 deletions(-)
19
20diff --git a/dns.c b/dns.c
21index e4f9bf830..9c9fe6413 100644
22--- a/dns.c
23+++ b/dns.c
24@@ -210,6 +210,7 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address,
25 {
26 u_int counter;
27 int result;
28+ unsigned int rrset_flags = 0;
29 struct rrsetinfo *fingerprints = NULL;
30
31 u_int8_t hostkey_algorithm;
32@@ -233,8 +234,19 @@ verify_host_key_dns(const char *hostname, struct sockaddr *address,
33 return -1;
34 }
35
36+ /*
37+ * Original getrrsetbyname function, found on OpenBSD for example,
38+ * doesn't accept any flag and prerequisite for obtaining AD bit in
39+ * DNS response is set by "options edns0" in resolv.conf.
40+ *
41+ * Our version is more clever and use RRSET_FORCE_EDNS0 flag.
42+ */
43+#ifndef HAVE_GETRRSETBYNAME
44+ rrset_flags |= RRSET_FORCE_EDNS0;
45+#endif
46 result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
47- DNS_RDATATYPE_SSHFP, 0, &fingerprints);
48+ DNS_RDATATYPE_SSHFP, rrset_flags, &fingerprints);
49+
50 if (result) {
51 verbose("DNS lookup error: %s", dns_result_totext(result));
52 return -1;
53diff --git a/openbsd-compat/getrrsetbyname.c b/openbsd-compat/getrrsetbyname.c
54index dc6fe0533..e061a290a 100644
55--- a/openbsd-compat/getrrsetbyname.c
56+++ b/openbsd-compat/getrrsetbyname.c
57@@ -209,8 +209,8 @@ getrrsetbyname(const char *hostname, unsigned int rdclass,
58 goto fail;
59 }
60
61- /* don't allow flags yet, unimplemented */
62- if (flags) {
63+ /* Allow RRSET_FORCE_EDNS0 flag only. */
64+ if ((flags & !RRSET_FORCE_EDNS0) != 0) {
65 result = ERRSET_INVAL;
66 goto fail;
67 }
68@@ -226,9 +226,9 @@ getrrsetbyname(const char *hostname, unsigned int rdclass,
69 #endif /* DEBUG */
70
71 #ifdef RES_USE_DNSSEC
72- /* turn on DNSSEC if EDNS0 is configured */
73- if (_resp->options & RES_USE_EDNS0)
74- _resp->options |= RES_USE_DNSSEC;
75+ /* turn on DNSSEC if required */
76+ if (flags & RRSET_FORCE_EDNS0)
77+ _resp->options |= (RES_USE_EDNS0|RES_USE_DNSSEC);
78 #endif /* RES_USE_DNSEC */
79
80 /* make query */
81diff --git a/openbsd-compat/getrrsetbyname.h b/openbsd-compat/getrrsetbyname.h
82index 1283f5506..dbbc85a2a 100644
83--- a/openbsd-compat/getrrsetbyname.h
84+++ b/openbsd-compat/getrrsetbyname.h
85@@ -72,6 +72,9 @@
86 #ifndef RRSET_VALIDATED
87 # define RRSET_VALIDATED 1
88 #endif
89+#ifndef RRSET_FORCE_EDNS0
90+# define RRSET_FORCE_EDNS0 0x0001
91+#endif
92
93 /*
94 * Return codes for getrrsetbyname()
diff --git a/debian/patches/doc-hash-tab-completion.patch b/debian/patches/doc-hash-tab-completion.patch
new file mode 100644
index 000000000..b0faea78c
--- /dev/null
+++ b/debian/patches/doc-hash-tab-completion.patch
@@ -0,0 +1,28 @@
1From a14ddfc3f607b0bf29046bfb4b26a6d827fa58c7 Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 9 Feb 2014 16:10:11 +0000
4Subject: Document that HashKnownHosts may break tab-completion
5
6Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1727
7Bug-Debian: http://bugs.debian.org/430154
8Last-Update: 2013-09-14
9
10Patch-Name: doc-hash-tab-completion.patch
11---
12 ssh_config.5 | 3 +++
13 1 file changed, 3 insertions(+)
14
15diff --git a/ssh_config.5 b/ssh_config.5
16index e61a0fd43..c6eaa63e7 100644
17--- a/ssh_config.5
18+++ b/ssh_config.5
19@@ -848,6 +848,9 @@ Note that existing names and addresses in known hosts files
20 will not be converted automatically,
21 but may be manually hashed using
22 .Xr ssh-keygen 1 .
23+Use of this option may break facilities such as tab-completion that rely
24+on being able to read unhashed host names from
25+.Pa ~/.ssh/known_hosts .
26 .It Cm HostbasedAuthentication
27 Specifies whether to try rhosts based authentication with public key
28 authentication.
diff --git a/debian/patches/gnome-ssh-askpass2-icon.patch b/debian/patches/gnome-ssh-askpass2-icon.patch
new file mode 100644
index 000000000..35b370752
--- /dev/null
+++ b/debian/patches/gnome-ssh-askpass2-icon.patch
@@ -0,0 +1,26 @@
1From 63da84c3570afb4fa6bab38fdac3e9af45d0ec54 Mon Sep 17 00:00:00 2001
2From: Vincent Untz <vuntz@ubuntu.com>
3Date: Sun, 9 Feb 2014 16:10:16 +0000
4Subject: Give the ssh-askpass-gnome window a default icon
5
6Bug-Ubuntu: https://bugs.launchpad.net/bugs/27152
7Last-Update: 2010-02-28
8
9Patch-Name: gnome-ssh-askpass2-icon.patch
10---
11 contrib/gnome-ssh-askpass2.c | 2 ++
12 1 file changed, 2 insertions(+)
13
14diff --git a/contrib/gnome-ssh-askpass2.c b/contrib/gnome-ssh-askpass2.c
15index bc83a2d67..88cdfaeff 100644
16--- a/contrib/gnome-ssh-askpass2.c
17+++ b/contrib/gnome-ssh-askpass2.c
18@@ -233,6 +233,8 @@ main(int argc, char **argv)
19
20 gtk_init(&argc, &argv);
21
22+ gtk_window_set_default_icon_from_file ("/usr/share/pixmaps/ssh-askpass-gnome.png", NULL);
23+
24 if (argc > 1) {
25 message = g_strjoinv(" ", argv + 1);
26 } else {
diff --git a/debian/patches/gssapi.patch b/debian/patches/gssapi.patch
new file mode 100644
index 000000000..4bf1d3f73
--- /dev/null
+++ b/debian/patches/gssapi.patch
@@ -0,0 +1,3983 @@
1From 34aff3aa136e5a65f441b25811dd466488fda087 Mon Sep 17 00:00:00 2001
2From: Simon Wilkinson <simon@sxw.org.uk>
3Date: Sun, 9 Feb 2014 16:09:48 +0000
4Subject: GSSAPI key exchange support
5
6This patch has been rejected upstream: "None of the OpenSSH developers are
7in favour of adding this, and this situation has not changed for several
8years. This is not a slight on Simon's patch, which is of fine quality, but
9just that a) we don't trust GSSAPI implementations that much and b) we don't
10like adding new KEX since they are pre-auth attack surface. This one is
11particularly scary, since it requires hooks out to typically root-owned
12system resources."
13
14However, quite a lot of people rely on this in Debian, and it's better to
15have it merged into the main openssh package rather than having separate
16-krb5 packages (as we used to have). It seems to have a generally good
17security history.
18
19Origin: other, https://github.com/openssh-gsskex/openssh-gsskex/commits/debian/master
20Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242
21Last-Updated: 2020-02-21
22
23Patch-Name: gssapi.patch
24---
25 Makefile.in | 3 +-
26 README.md | 33 +++
27 auth.c | 96 +-------
28 auth2-gss.c | 56 ++++-
29 auth2.c | 2 +
30 canohost.c | 93 ++++++++
31 canohost.h | 3 +
32 clientloop.c | 15 +-
33 configure.ac | 24 ++
34 gss-genr.c | 300 +++++++++++++++++++++++-
35 gss-serv-krb5.c | 85 ++++++-
36 gss-serv.c | 186 +++++++++++++--
37 kex.c | 66 +++++-
38 kex.h | 29 +++
39 kexdh.c | 10 +
40 kexgen.c | 2 +-
41 kexgssc.c | 606 ++++++++++++++++++++++++++++++++++++++++++++++++
42 kexgsss.c | 474 +++++++++++++++++++++++++++++++++++++
43 monitor.c | 139 ++++++++++-
44 monitor.h | 2 +
45 monitor_wrap.c | 57 ++++-
46 monitor_wrap.h | 4 +-
47 readconf.c | 70 ++++++
48 readconf.h | 6 +
49 servconf.c | 47 ++++
50 servconf.h | 3 +
51 session.c | 10 +-
52 ssh-gss.h | 50 +++-
53 ssh.1 | 8 +
54 ssh.c | 6 +-
55 ssh_config | 2 +
56 ssh_config.5 | 57 +++++
57 sshconnect2.c | 142 +++++++++++-
58 sshd.c | 62 ++++-
59 sshd_config | 2 +
60 sshd_config.5 | 30 +++
61 sshkey.c | 3 +-
62 sshkey.h | 1 +
63 38 files changed, 2624 insertions(+), 160 deletions(-)
64 create mode 100644 kexgssc.c
65 create mode 100644 kexgsss.c
66
67diff --git a/Makefile.in b/Makefile.in
68index e7549470c..b68c1710f 100644
69--- a/Makefile.in
70+++ b/Makefile.in
71@@ -109,6 +109,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
72 kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
73 kexgexc.o kexgexs.o \
74 sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \
75+ kexgssc.o \
76 sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
77 sshbuf-io.o
78
79@@ -125,7 +126,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
80 auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
81 auth2-none.o auth2-passwd.o auth2-pubkey.o \
82 monitor.o monitor_wrap.o auth-krb5.o \
83- auth2-gss.o gss-serv.o gss-serv-krb5.o \
84+ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
85 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
86 sftp-server.o sftp-common.o \
87 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
88diff --git a/README.md b/README.md
89index 28fb43d2a..5b73d24c0 100644
90--- a/README.md
91+++ b/README.md
92@@ -1,3 +1,36 @@
93+Portable OpenSSH with GSSAPI Key Exchange patches
94+=================================================
95+
96+Currently, there are two branches with gssapi key exchange related
97+patches:
98+
99+ * fedora/master: Changes that are shipped in Fedora
100+ * debian/master: Changes that are shipped in Debian
101+
102+The target is to converge to a shared repository with single master
103+branch from where we could build releases for both OSes.
104+
105+
106+What is in:
107+
108+ * The original patch implementing missing parts of RFC4462 by Simon Wilkinson
109+ adapted to the current OpenSSH versions and with several fixes
110+ * New methods for GSSAPI Kex from IETF draft [1] from Jakub Jelen
111+
112+
113+Missing kerberos-related parts:
114+
115+ * .k5login and .kusers support available in Fedora [2] [3].
116+ * Improved handling of kerberos ccache location [4]
117+
118+
119+[1] https://tools.ietf.org/html/draft-ietf-curdle-gss-keyex-sha2-08
120+[2] https://src.fedoraproject.org/rpms/openssh/blob/master/f/openssh-6.6p1-kuserok.patch
121+[3] https://src.fedoraproject.org/rpms/openssh/blob/master/f/openssh-6.6p1-GSSAPIEnablek5users.patch
122+[4] https://bugzilla.mindrot.org/show_bug.cgi?id=2775
123+
124+-------------------------------------------------------------------------------
125+
126 # Portable OpenSSH
127
128 [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh)
129diff --git a/auth.c b/auth.c
130index 086b8ebb1..687c57b42 100644
131--- a/auth.c
132+++ b/auth.c
133@@ -400,7 +400,8 @@ auth_root_allowed(struct ssh *ssh, const char *method)
134 case PERMIT_NO_PASSWD:
135 if (strcmp(method, "publickey") == 0 ||
136 strcmp(method, "hostbased") == 0 ||
137- strcmp(method, "gssapi-with-mic") == 0)
138+ strcmp(method, "gssapi-with-mic") == 0 ||
139+ strcmp(method, "gssapi-keyex") == 0)
140 return 1;
141 break;
142 case PERMIT_FORCED_ONLY:
143@@ -724,99 +725,6 @@ fakepw(void)
144 return (&fake);
145 }
146
147-/*
148- * Returns the remote DNS hostname as a string. The returned string must not
149- * be freed. NB. this will usually trigger a DNS query the first time it is
150- * called.
151- * This function does additional checks on the hostname to mitigate some
152- * attacks on legacy rhosts-style authentication.
153- * XXX is RhostsRSAAuthentication vulnerable to these?
154- * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
155- */
156-
157-static char *
158-remote_hostname(struct ssh *ssh)
159-{
160- struct sockaddr_storage from;
161- socklen_t fromlen;
162- struct addrinfo hints, *ai, *aitop;
163- char name[NI_MAXHOST], ntop2[NI_MAXHOST];
164- const char *ntop = ssh_remote_ipaddr(ssh);
165-
166- /* Get IP address of client. */
167- fromlen = sizeof(from);
168- memset(&from, 0, sizeof(from));
169- if (getpeername(ssh_packet_get_connection_in(ssh),
170- (struct sockaddr *)&from, &fromlen) == -1) {
171- debug("getpeername failed: %.100s", strerror(errno));
172- return xstrdup(ntop);
173- }
174-
175- ipv64_normalise_mapped(&from, &fromlen);
176- if (from.ss_family == AF_INET6)
177- fromlen = sizeof(struct sockaddr_in6);
178-
179- debug3("Trying to reverse map address %.100s.", ntop);
180- /* Map the IP address to a host name. */
181- if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
182- NULL, 0, NI_NAMEREQD) != 0) {
183- /* Host name not found. Use ip address. */
184- return xstrdup(ntop);
185- }
186-
187- /*
188- * if reverse lookup result looks like a numeric hostname,
189- * someone is trying to trick us by PTR record like following:
190- * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
191- */
192- memset(&hints, 0, sizeof(hints));
193- hints.ai_socktype = SOCK_DGRAM; /*dummy*/
194- hints.ai_flags = AI_NUMERICHOST;
195- if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
196- logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
197- name, ntop);
198- freeaddrinfo(ai);
199- return xstrdup(ntop);
200- }
201-
202- /* Names are stored in lowercase. */
203- lowercase(name);
204-
205- /*
206- * Map it back to an IP address and check that the given
207- * address actually is an address of this host. This is
208- * necessary because anyone with access to a name server can
209- * define arbitrary names for an IP address. Mapping from
210- * name to IP address can be trusted better (but can still be
211- * fooled if the intruder has access to the name server of
212- * the domain).
213- */
214- memset(&hints, 0, sizeof(hints));
215- hints.ai_family = from.ss_family;
216- hints.ai_socktype = SOCK_STREAM;
217- if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
218- logit("reverse mapping checking getaddrinfo for %.700s "
219- "[%s] failed.", name, ntop);
220- return xstrdup(ntop);
221- }
222- /* Look for the address from the list of addresses. */
223- for (ai = aitop; ai; ai = ai->ai_next) {
224- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
225- sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
226- (strcmp(ntop, ntop2) == 0))
227- break;
228- }
229- freeaddrinfo(aitop);
230- /* If we reached the end of the list, the address was not there. */
231- if (ai == NULL) {
232- /* Address not found for the host name. */
233- logit("Address %.100s maps to %.600s, but this does not "
234- "map back to the address.", ntop, name);
235- return xstrdup(ntop);
236- }
237- return xstrdup(name);
238-}
239-
240 /*
241 * Return the canonical name of the host in the other side of the current
242 * connection. The host name is cached, so it is efficient to call this
243diff --git a/auth2-gss.c b/auth2-gss.c
244index 9351e0428..d6446c0cf 100644
245--- a/auth2-gss.c
246+++ b/auth2-gss.c
247@@ -1,7 +1,7 @@
248 /* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */
249
250 /*
251- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
252+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
253 *
254 * Redistribution and use in source and binary forms, with or without
255 * modification, are permitted provided that the following conditions
256@@ -54,6 +54,48 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
257 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
258 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
259
260+/*
261+ * The 'gssapi_keyex' userauth mechanism.
262+ */
263+static int
264+userauth_gsskeyex(struct ssh *ssh)
265+{
266+ Authctxt *authctxt = ssh->authctxt;
267+ int r, authenticated = 0;
268+ struct sshbuf *b = NULL;
269+ gss_buffer_desc mic, gssbuf;
270+ u_char *p;
271+ size_t len;
272+
273+ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
274+ (r = sshpkt_get_end(ssh)) != 0)
275+ fatal("%s: %s", __func__, ssh_err(r));
276+
277+ if ((b = sshbuf_new()) == NULL)
278+ fatal("%s: sshbuf_new failed", __func__);
279+
280+ mic.value = p;
281+ mic.length = len;
282+
283+ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
284+ "gssapi-keyex");
285+
286+ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
287+ fatal("%s: sshbuf_mutable_ptr failed", __func__);
288+ gssbuf.length = sshbuf_len(b);
289+
290+ /* gss_kex_context is NULL with privsep, so we can't check it here */
291+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
292+ &gssbuf, &mic))))
293+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
294+ authctxt->pw, 1));
295+
296+ sshbuf_free(b);
297+ free(mic.value);
298+
299+ return (authenticated);
300+}
301+
302 /*
303 * We only support those mechanisms that we know about (ie ones that we know
304 * how to check local user kuserok and the like)
305@@ -260,7 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
306 if ((r = sshpkt_get_end(ssh)) != 0)
307 fatal("%s: %s", __func__, ssh_err(r));
308
309- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
310+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
311+ authctxt->pw, 1));
312
313 if ((!use_privsep || mm_is_monitor()) &&
314 (displayname = ssh_gssapi_displayname()) != NULL)
315@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
316 gssbuf.length = sshbuf_len(b);
317
318 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
319- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
320+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
321+ authctxt->pw, 0));
322 else
323 logit("GSSAPI MIC check failed");
324
325@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
326 return 0;
327 }
328
329+Authmethod method_gsskeyex = {
330+ "gssapi-keyex",
331+ userauth_gsskeyex,
332+ &options.gss_authentication
333+};
334+
335 Authmethod method_gssapi = {
336 "gssapi-with-mic",
337 userauth_gssapi,
338diff --git a/auth2.c b/auth2.c
339index 0e7762242..1c217268c 100644
340--- a/auth2.c
341+++ b/auth2.c
342@@ -73,6 +73,7 @@ extern Authmethod method_passwd;
343 extern Authmethod method_kbdint;
344 extern Authmethod method_hostbased;
345 #ifdef GSSAPI
346+extern Authmethod method_gsskeyex;
347 extern Authmethod method_gssapi;
348 #endif
349
350@@ -80,6 +81,7 @@ Authmethod *authmethods[] = {
351 &method_none,
352 &method_pubkey,
353 #ifdef GSSAPI
354+ &method_gsskeyex,
355 &method_gssapi,
356 #endif
357 &method_passwd,
358diff --git a/canohost.c b/canohost.c
359index abea9c6e6..8e81b5193 100644
360--- a/canohost.c
361+++ b/canohost.c
362@@ -35,6 +35,99 @@
363 #include "canohost.h"
364 #include "misc.h"
365
366+/*
367+ * Returns the remote DNS hostname as a string. The returned string must not
368+ * be freed. NB. this will usually trigger a DNS query the first time it is
369+ * called.
370+ * This function does additional checks on the hostname to mitigate some
371+ * attacks on legacy rhosts-style authentication.
372+ * XXX is RhostsRSAAuthentication vulnerable to these?
373+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
374+ */
375+
376+char *
377+remote_hostname(struct ssh *ssh)
378+{
379+ struct sockaddr_storage from;
380+ socklen_t fromlen;
381+ struct addrinfo hints, *ai, *aitop;
382+ char name[NI_MAXHOST], ntop2[NI_MAXHOST];
383+ const char *ntop = ssh_remote_ipaddr(ssh);
384+
385+ /* Get IP address of client. */
386+ fromlen = sizeof(from);
387+ memset(&from, 0, sizeof(from));
388+ if (getpeername(ssh_packet_get_connection_in(ssh),
389+ (struct sockaddr *)&from, &fromlen) == -1) {
390+ debug("getpeername failed: %.100s", strerror(errno));
391+ return xstrdup(ntop);
392+ }
393+
394+ ipv64_normalise_mapped(&from, &fromlen);
395+ if (from.ss_family == AF_INET6)
396+ fromlen = sizeof(struct sockaddr_in6);
397+
398+ debug3("Trying to reverse map address %.100s.", ntop);
399+ /* Map the IP address to a host name. */
400+ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
401+ NULL, 0, NI_NAMEREQD) != 0) {
402+ /* Host name not found. Use ip address. */
403+ return xstrdup(ntop);
404+ }
405+
406+ /*
407+ * if reverse lookup result looks like a numeric hostname,
408+ * someone is trying to trick us by PTR record like following:
409+ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
410+ */
411+ memset(&hints, 0, sizeof(hints));
412+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
413+ hints.ai_flags = AI_NUMERICHOST;
414+ if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
415+ logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
416+ name, ntop);
417+ freeaddrinfo(ai);
418+ return xstrdup(ntop);
419+ }
420+
421+ /* Names are stored in lowercase. */
422+ lowercase(name);
423+
424+ /*
425+ * Map it back to an IP address and check that the given
426+ * address actually is an address of this host. This is
427+ * necessary because anyone with access to a name server can
428+ * define arbitrary names for an IP address. Mapping from
429+ * name to IP address can be trusted better (but can still be
430+ * fooled if the intruder has access to the name server of
431+ * the domain).
432+ */
433+ memset(&hints, 0, sizeof(hints));
434+ hints.ai_family = from.ss_family;
435+ hints.ai_socktype = SOCK_STREAM;
436+ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
437+ logit("reverse mapping checking getaddrinfo for %.700s "
438+ "[%s] failed.", name, ntop);
439+ return xstrdup(ntop);
440+ }
441+ /* Look for the address from the list of addresses. */
442+ for (ai = aitop; ai; ai = ai->ai_next) {
443+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
444+ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
445+ (strcmp(ntop, ntop2) == 0))
446+ break;
447+ }
448+ freeaddrinfo(aitop);
449+ /* If we reached the end of the list, the address was not there. */
450+ if (ai == NULL) {
451+ /* Address not found for the host name. */
452+ logit("Address %.100s maps to %.600s, but this does not "
453+ "map back to the address.", ntop, name);
454+ return xstrdup(ntop);
455+ }
456+ return xstrdup(name);
457+}
458+
459 void
460 ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
461 {
462diff --git a/canohost.h b/canohost.h
463index 26d62855a..0cadc9f18 100644
464--- a/canohost.h
465+++ b/canohost.h
466@@ -15,6 +15,9 @@
467 #ifndef _CANOHOST_H
468 #define _CANOHOST_H
469
470+struct ssh;
471+
472+char *remote_hostname(struct ssh *);
473 char *get_peer_ipaddr(int);
474 int get_peer_port(int);
475 char *get_local_ipaddr(int);
476diff --git a/clientloop.c b/clientloop.c
477index ebd0dbca1..1bdac6a46 100644
478--- a/clientloop.c
479+++ b/clientloop.c
480@@ -112,6 +112,10 @@
481 #include "ssherr.h"
482 #include "hostfile.h"
483
484+#ifdef GSSAPI
485+#include "ssh-gss.h"
486+#endif
487+
488 /* import options */
489 extern Options options;
490
491@@ -1379,9 +1383,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
492 break;
493
494 /* Do channel operations unless rekeying in progress. */
495- if (!ssh_packet_is_rekeying(ssh))
496+ if (!ssh_packet_is_rekeying(ssh)) {
497 channel_after_select(ssh, readset, writeset);
498
499+#ifdef GSSAPI
500+ if (options.gss_renewal_rekey &&
501+ ssh_gssapi_credentials_updated(NULL)) {
502+ debug("credentials updated - forcing rekey");
503+ need_rekeying = 1;
504+ }
505+#endif
506+ }
507+
508 /* Buffer input from the connection. */
509 client_process_net_input(ssh, readset);
510
511diff --git a/configure.ac b/configure.ac
512index b689db4b5..efafb6bd8 100644
513--- a/configure.ac
514+++ b/configure.ac
515@@ -674,6 +674,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
516 [Use tunnel device compatibility to OpenBSD])
517 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
518 [Prepend the address family to IP tunnel traffic])
519+ AC_MSG_CHECKING([if we have the Security Authorization Session API])
520+ AC_TRY_COMPILE([#include <Security/AuthSession.h>],
521+ [SessionCreate(0, 0);],
522+ [ac_cv_use_security_session_api="yes"
523+ AC_DEFINE([USE_SECURITY_SESSION_API], [1],
524+ [platform has the Security Authorization Session API])
525+ LIBS="$LIBS -framework Security"
526+ AC_MSG_RESULT([yes])],
527+ [ac_cv_use_security_session_api="no"
528+ AC_MSG_RESULT([no])])
529+ AC_MSG_CHECKING([if we have an in-memory credentials cache])
530+ AC_TRY_COMPILE(
531+ [#include <Kerberos/Kerberos.h>],
532+ [cc_context_t c;
533+ (void) cc_initialize (&c, 0, NULL, NULL);],
534+ [AC_DEFINE([USE_CCAPI], [1],
535+ [platform uses an in-memory credentials cache])
536+ LIBS="$LIBS -framework Security"
537+ AC_MSG_RESULT([yes])
538+ if test "x$ac_cv_use_security_session_api" = "xno"; then
539+ AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
540+ fi],
541+ [AC_MSG_RESULT([no])]
542+ )
543 m4_pattern_allow([AU_IPv])
544 AC_CHECK_DECL([AU_IPv4], [],
545 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
546diff --git a/gss-genr.c b/gss-genr.c
547index d56257b4a..763a63ffa 100644
548--- a/gss-genr.c
549+++ b/gss-genr.c
550@@ -1,7 +1,7 @@
551 /* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm Exp $ */
552
553 /*
554- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
555+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
556 *
557 * Redistribution and use in source and binary forms, with or without
558 * modification, are permitted provided that the following conditions
559@@ -41,12 +41,36 @@
560 #include "sshbuf.h"
561 #include "log.h"
562 #include "ssh2.h"
563+#include "cipher.h"
564+#include "sshkey.h"
565+#include "kex.h"
566+#include "digest.h"
567+#include "packet.h"
568
569 #include "ssh-gss.h"
570
571 extern u_char *session_id2;
572 extern u_int session_id2_len;
573
574+typedef struct {
575+ char *encoded;
576+ gss_OID oid;
577+} ssh_gss_kex_mapping;
578+
579+/*
580+ * XXX - It would be nice to find a more elegant way of handling the
581+ * XXX passing of the key exchange context to the userauth routines
582+ */
583+
584+Gssctxt *gss_kex_context = NULL;
585+
586+static ssh_gss_kex_mapping *gss_enc2oid = NULL;
587+
588+int
589+ssh_gssapi_oid_table_ok(void) {
590+ return (gss_enc2oid != NULL);
591+}
592+
593 /* sshbuf_get for gss_buffer_desc */
594 int
595 ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
596@@ -62,6 +86,162 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
597 return 0;
598 }
599
600+/* sshpkt_get of gss_buffer_desc */
601+int
602+ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g)
603+{
604+ int r;
605+ u_char *p;
606+ size_t len;
607+
608+ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
609+ return r;
610+ g->value = p;
611+ g->length = len;
612+ return 0;
613+}
614+
615+/*
616+ * Return a list of the gss-group1-sha1 mechanisms supported by this program
617+ *
618+ * We test mechanisms to ensure that we can use them, to avoid starting
619+ * a key exchange with a bad mechanism
620+ */
621+
622+char *
623+ssh_gssapi_client_mechanisms(const char *host, const char *client,
624+ const char *kex) {
625+ gss_OID_set gss_supported = NULL;
626+ OM_uint32 min_status;
627+
628+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
629+ return NULL;
630+
631+ return ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
632+ host, client, kex);
633+}
634+
635+char *
636+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
637+ const char *host, const char *client, const char *kex) {
638+ struct sshbuf *buf = NULL;
639+ size_t i;
640+ int r = SSH_ERR_ALLOC_FAIL;
641+ int oidpos, enclen;
642+ char *mechs, *encoded;
643+ u_char digest[SSH_DIGEST_MAX_LENGTH];
644+ char deroid[2];
645+ struct ssh_digest_ctx *md = NULL;
646+ char *s, *cp, *p;
647+
648+ if (gss_enc2oid != NULL) {
649+ for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
650+ free(gss_enc2oid[i].encoded);
651+ free(gss_enc2oid);
652+ }
653+
654+ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
655+ (gss_supported->count + 1));
656+
657+ if ((buf = sshbuf_new()) == NULL)
658+ fatal("%s: sshbuf_new failed", __func__);
659+
660+ oidpos = 0;
661+ s = cp = xstrdup(kex);
662+ for (i = 0; i < gss_supported->count; i++) {
663+ if (gss_supported->elements[i].length < 128 &&
664+ (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
665+
666+ deroid[0] = SSH_GSS_OIDTYPE;
667+ deroid[1] = gss_supported->elements[i].length;
668+
669+ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
670+ (r = ssh_digest_update(md, deroid, 2)) != 0 ||
671+ (r = ssh_digest_update(md,
672+ gss_supported->elements[i].elements,
673+ gss_supported->elements[i].length)) != 0 ||
674+ (r = ssh_digest_final(md, digest, sizeof(digest))) != 0)
675+ fatal("%s: digest failed: %s", __func__,
676+ ssh_err(r));
677+ ssh_digest_free(md);
678+ md = NULL;
679+
680+ encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
681+ * 2);
682+ enclen = __b64_ntop(digest,
683+ ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
684+ ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
685+
686+ cp = strncpy(s, kex, strlen(kex));
687+ for ((p = strsep(&cp, ",")); p && *p != '\0';
688+ (p = strsep(&cp, ","))) {
689+ if (sshbuf_len(buf) != 0 &&
690+ (r = sshbuf_put_u8(buf, ',')) != 0)
691+ fatal("%s: sshbuf_put_u8 error: %s",
692+ __func__, ssh_err(r));
693+ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 ||
694+ (r = sshbuf_put(buf, encoded, enclen)) != 0)
695+ fatal("%s: sshbuf_put error: %s",
696+ __func__, ssh_err(r));
697+ }
698+
699+ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
700+ gss_enc2oid[oidpos].encoded = encoded;
701+ oidpos++;
702+ }
703+ }
704+ free(s);
705+ gss_enc2oid[oidpos].oid = NULL;
706+ gss_enc2oid[oidpos].encoded = NULL;
707+
708+ if ((mechs = sshbuf_dup_string(buf)) == NULL)
709+ fatal("%s: sshbuf_dup_string failed", __func__);
710+
711+ sshbuf_free(buf);
712+
713+ if (strlen(mechs) == 0) {
714+ free(mechs);
715+ mechs = NULL;
716+ }
717+
718+ return (mechs);
719+}
720+
721+gss_OID
722+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
723+ int i = 0;
724+
725+#define SKIP_KEX_NAME(type) \
726+ case type: \
727+ if (strlen(name) < sizeof(type##_ID)) \
728+ return GSS_C_NO_OID; \
729+ name += sizeof(type##_ID) - 1; \
730+ break;
731+
732+ switch (kex_type) {
733+ SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1)
734+ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1)
735+ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256)
736+ SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512)
737+ SKIP_KEX_NAME(KEX_GSS_GEX_SHA1)
738+ SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256)
739+ SKIP_KEX_NAME(KEX_GSS_C25519_SHA256)
740+ default:
741+ return GSS_C_NO_OID;
742+ }
743+
744+#undef SKIP_KEX_NAME
745+
746+ while (gss_enc2oid[i].encoded != NULL &&
747+ strcmp(name, gss_enc2oid[i].encoded) != 0)
748+ i++;
749+
750+ if (gss_enc2oid[i].oid != NULL && ctx != NULL)
751+ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
752+
753+ return gss_enc2oid[i].oid;
754+}
755+
756 /* Check that the OID in a data stream matches that in the context */
757 int
758 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
759@@ -218,7 +398,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
760 }
761
762 ctx->major = gss_init_sec_context(&ctx->minor,
763- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid,
764+ ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
765 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
766 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
767
768@@ -247,9 +427,43 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
769 return (ctx->major);
770 }
771
772+OM_uint32
773+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
774+{
775+ gss_buffer_desc gssbuf;
776+ gss_name_t gssname;
777+ OM_uint32 status;
778+ gss_OID_set oidset;
779+
780+ gssbuf.value = (void *) name;
781+ gssbuf.length = strlen(gssbuf.value);
782+
783+ gss_create_empty_oid_set(&status, &oidset);
784+ gss_add_oid_set_member(&status, ctx->oid, &oidset);
785+
786+ ctx->major = gss_import_name(&ctx->minor, &gssbuf,
787+ GSS_C_NT_USER_NAME, &gssname);
788+
789+ if (!ctx->major)
790+ ctx->major = gss_acquire_cred(&ctx->minor,
791+ gssname, 0, oidset, GSS_C_INITIATE,
792+ &ctx->client_creds, NULL, NULL);
793+
794+ gss_release_name(&status, &gssname);
795+ gss_release_oid_set(&status, &oidset);
796+
797+ if (ctx->major)
798+ ssh_gssapi_error(ctx);
799+
800+ return(ctx->major);
801+}
802+
803 OM_uint32
804 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
805 {
806+ if (ctx == NULL)
807+ return -1;
808+
809 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
810 GSS_C_QOP_DEFAULT, buffer, hash)))
811 ssh_gssapi_error(ctx);
812@@ -257,6 +471,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
813 return (ctx->major);
814 }
815
816+/* Priviledged when used by server */
817+OM_uint32
818+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
819+{
820+ if (ctx == NULL)
821+ return -1;
822+
823+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
824+ gssbuf, gssmic, NULL);
825+
826+ return (ctx->major);
827+}
828+
829 void
830 ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
831 const char *context)
832@@ -273,11 +500,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
833 }
834
835 int
836-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
837+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
838+ const char *client)
839 {
840 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
841 OM_uint32 major, minor;
842 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
843+ Gssctxt *intctx = NULL;
844+
845+ if (ctx == NULL)
846+ ctx = &intctx;
847
848 /* RFC 4462 says we MUST NOT do SPNEGO */
849 if (oid->length == spnego_oid.length &&
850@@ -287,6 +519,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
851 ssh_gssapi_build_ctx(ctx);
852 ssh_gssapi_set_oid(*ctx, oid);
853 major = ssh_gssapi_import_name(*ctx, host);
854+
855+ if (!GSS_ERROR(major) && client)
856+ major = ssh_gssapi_client_identity(*ctx, client);
857+
858 if (!GSS_ERROR(major)) {
859 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
860 NULL);
861@@ -296,10 +532,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
862 GSS_C_NO_BUFFER);
863 }
864
865- if (GSS_ERROR(major))
866+ if (GSS_ERROR(major) || intctx != NULL)
867 ssh_gssapi_delete_ctx(ctx);
868
869 return (!GSS_ERROR(major));
870 }
871
872+int
873+ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
874+ static gss_name_t saved_name = GSS_C_NO_NAME;
875+ static OM_uint32 saved_lifetime = 0;
876+ static gss_OID saved_mech = GSS_C_NO_OID;
877+ static gss_name_t name;
878+ static OM_uint32 last_call = 0;
879+ OM_uint32 lifetime, now, major, minor;
880+ int equal;
881+
882+ now = time(NULL);
883+
884+ if (ctxt) {
885+ debug("Rekey has happened - updating saved versions");
886+
887+ if (saved_name != GSS_C_NO_NAME)
888+ gss_release_name(&minor, &saved_name);
889+
890+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
891+ &saved_name, &saved_lifetime, NULL, NULL);
892+
893+ if (!GSS_ERROR(major)) {
894+ saved_mech = ctxt->oid;
895+ saved_lifetime+= now;
896+ } else {
897+ /* Handle the error */
898+ }
899+ return 0;
900+ }
901+
902+ if (now - last_call < 10)
903+ return 0;
904+
905+ last_call = now;
906+
907+ if (saved_mech == GSS_C_NO_OID)
908+ return 0;
909+
910+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
911+ &name, &lifetime, NULL, NULL);
912+ if (major == GSS_S_CREDENTIALS_EXPIRED)
913+ return 0;
914+ else if (GSS_ERROR(major))
915+ return 0;
916+
917+ major = gss_compare_name(&minor, saved_name, name, &equal);
918+ gss_release_name(&minor, &name);
919+ if (GSS_ERROR(major))
920+ return 0;
921+
922+ if (equal && (saved_lifetime < lifetime + now - 10))
923+ return 1;
924+
925+ return 0;
926+}
927+
928 #endif /* GSSAPI */
929diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
930index a151bc1e4..ef9beb67c 100644
931--- a/gss-serv-krb5.c
932+++ b/gss-serv-krb5.c
933@@ -1,7 +1,7 @@
934 /* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */
935
936 /*
937- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
938+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
939 *
940 * Redistribution and use in source and binary forms, with or without
941 * modification, are permitted provided that the following conditions
942@@ -120,8 +120,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
943 krb5_error_code problem;
944 krb5_principal princ;
945 OM_uint32 maj_status, min_status;
946- int len;
947 const char *errmsg;
948+ const char *new_ccname;
949
950 if (client->creds == NULL) {
951 debug("No credentials stored");
952@@ -180,11 +180,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
953 return;
954 }
955
956- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
957+ new_ccname = krb5_cc_get_name(krb_context, ccache);
958+
959 client->store.envvar = "KRB5CCNAME";
960- len = strlen(client->store.filename) + 6;
961- client->store.envval = xmalloc(len);
962- snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
963+#ifdef USE_CCAPI
964+ xasprintf(&client->store.envval, "API:%s", new_ccname);
965+ client->store.filename = NULL;
966+#else
967+ xasprintf(&client->store.envval, "FILE:%s", new_ccname);
968+ client->store.filename = xstrdup(new_ccname);
969+#endif
970
971 #ifdef USE_PAM
972 if (options.use_pam)
973@@ -196,6 +201,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
974 return;
975 }
976
977+int
978+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
979+ ssh_gssapi_client *client)
980+{
981+ krb5_ccache ccache = NULL;
982+ krb5_principal principal = NULL;
983+ char *name = NULL;
984+ krb5_error_code problem;
985+ OM_uint32 maj_status, min_status;
986+
987+ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
988+ logit("krb5_cc_resolve(): %.100s",
989+ krb5_get_err_text(krb_context, problem));
990+ return 0;
991+ }
992+
993+ /* Find out who the principal in this cache is */
994+ if ((problem = krb5_cc_get_principal(krb_context, ccache,
995+ &principal))) {
996+ logit("krb5_cc_get_principal(): %.100s",
997+ krb5_get_err_text(krb_context, problem));
998+ krb5_cc_close(krb_context, ccache);
999+ return 0;
1000+ }
1001+
1002+ if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
1003+ logit("krb5_unparse_name(): %.100s",
1004+ krb5_get_err_text(krb_context, problem));
1005+ krb5_free_principal(krb_context, principal);
1006+ krb5_cc_close(krb_context, ccache);
1007+ return 0;
1008+ }
1009+
1010+
1011+ if (strcmp(name,client->exportedname.value)!=0) {
1012+ debug("Name in local credentials cache differs. Not storing");
1013+ krb5_free_principal(krb_context, principal);
1014+ krb5_cc_close(krb_context, ccache);
1015+ krb5_free_unparsed_name(krb_context, name);
1016+ return 0;
1017+ }
1018+ krb5_free_unparsed_name(krb_context, name);
1019+
1020+ /* Name matches, so lets get on with it! */
1021+
1022+ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
1023+ logit("krb5_cc_initialize(): %.100s",
1024+ krb5_get_err_text(krb_context, problem));
1025+ krb5_free_principal(krb_context, principal);
1026+ krb5_cc_close(krb_context, ccache);
1027+ return 0;
1028+ }
1029+
1030+ krb5_free_principal(krb_context, principal);
1031+
1032+ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
1033+ ccache))) {
1034+ logit("gss_krb5_copy_ccache() failed. Sorry!");
1035+ krb5_cc_close(krb_context, ccache);
1036+ return 0;
1037+ }
1038+
1039+ return 1;
1040+}
1041+
1042 ssh_gssapi_mech gssapi_kerberos_mech = {
1043 "toWM5Slw5Ew8Mqkay+al2g==",
1044 "Kerberos",
1045@@ -203,7 +273,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
1046 NULL,
1047 &ssh_gssapi_krb5_userok,
1048 NULL,
1049- &ssh_gssapi_krb5_storecreds
1050+ &ssh_gssapi_krb5_storecreds,
1051+ &ssh_gssapi_krb5_updatecreds
1052 };
1053
1054 #endif /* KRB5 */
1055diff --git a/gss-serv.c b/gss-serv.c
1056index ab3a15f0f..1d47870e7 100644
1057--- a/gss-serv.c
1058+++ b/gss-serv.c
1059@@ -1,7 +1,7 @@
1060 /* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */
1061
1062 /*
1063- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
1064+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1065 *
1066 * Redistribution and use in source and binary forms, with or without
1067 * modification, are permitted provided that the following conditions
1068@@ -44,17 +44,19 @@
1069 #include "session.h"
1070 #include "misc.h"
1071 #include "servconf.h"
1072+#include "uidswap.h"
1073
1074 #include "ssh-gss.h"
1075+#include "monitor_wrap.h"
1076
1077 extern ServerOptions options;
1078
1079 static ssh_gssapi_client gssapi_client =
1080- { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
1081- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
1082+ { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL,
1083+ GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0};
1084
1085 ssh_gssapi_mech gssapi_null_mech =
1086- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
1087+ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
1088
1089 #ifdef KRB5
1090 extern ssh_gssapi_mech gssapi_kerberos_mech;
1091@@ -140,6 +142,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
1092 return (ssh_gssapi_acquire_cred(*ctx));
1093 }
1094
1095+/* Unprivileged */
1096+char *
1097+ssh_gssapi_server_mechanisms(void) {
1098+ if (supported_oids == NULL)
1099+ ssh_gssapi_prepare_supported_oids();
1100+ return (ssh_gssapi_kex_mechs(supported_oids,
1101+ &ssh_gssapi_server_check_mech, NULL, NULL,
1102+ options.gss_kex_algorithms));
1103+}
1104+
1105+/* Unprivileged */
1106+int
1107+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
1108+ const char *dummy) {
1109+ Gssctxt *ctx = NULL;
1110+ int res;
1111+
1112+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
1113+ ssh_gssapi_delete_ctx(&ctx);
1114+
1115+ return (res);
1116+}
1117+
1118 /* Unprivileged */
1119 void
1120 ssh_gssapi_supported_oids(gss_OID_set *oidset)
1121@@ -150,7 +175,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
1122 gss_OID_set supported;
1123
1124 gss_create_empty_oid_set(&min_status, oidset);
1125- gss_indicate_mechs(&min_status, &supported);
1126+
1127+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
1128+ return;
1129
1130 while (supported_mechs[i]->name != NULL) {
1131 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
1132@@ -276,8 +303,48 @@ OM_uint32
1133 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1134 {
1135 int i = 0;
1136+ int equal = 0;
1137+ gss_name_t new_name = GSS_C_NO_NAME;
1138+ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
1139+
1140+ if (options.gss_store_rekey && client->used && ctx->client_creds) {
1141+ if (client->mech->oid.length != ctx->oid->length ||
1142+ (memcmp(client->mech->oid.elements,
1143+ ctx->oid->elements, ctx->oid->length) !=0)) {
1144+ debug("Rekeyed credentials have different mechanism");
1145+ return GSS_S_COMPLETE;
1146+ }
1147+
1148+ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
1149+ ctx->client_creds, ctx->oid, &new_name,
1150+ NULL, NULL, NULL))) {
1151+ ssh_gssapi_error(ctx);
1152+ return (ctx->major);
1153+ }
1154+
1155+ ctx->major = gss_compare_name(&ctx->minor, client->name,
1156+ new_name, &equal);
1157+
1158+ if (GSS_ERROR(ctx->major)) {
1159+ ssh_gssapi_error(ctx);
1160+ return (ctx->major);
1161+ }
1162+
1163+ if (!equal) {
1164+ debug("Rekeyed credentials have different name");
1165+ return GSS_S_COMPLETE;
1166+ }
1167
1168- gss_buffer_desc ename;
1169+ debug("Marking rekeyed credentials for export");
1170+
1171+ gss_release_name(&ctx->minor, &client->name);
1172+ gss_release_cred(&ctx->minor, &client->creds);
1173+ client->name = new_name;
1174+ client->creds = ctx->client_creds;
1175+ ctx->client_creds = GSS_C_NO_CREDENTIAL;
1176+ client->updated = 1;
1177+ return GSS_S_COMPLETE;
1178+ }
1179
1180 client->mech = NULL;
1181
1182@@ -292,6 +359,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1183 if (client->mech == NULL)
1184 return GSS_S_FAILURE;
1185
1186+ if (ctx->client_creds &&
1187+ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
1188+ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
1189+ ssh_gssapi_error(ctx);
1190+ return (ctx->major);
1191+ }
1192+
1193 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
1194 &client->displayname, NULL))) {
1195 ssh_gssapi_error(ctx);
1196@@ -309,6 +383,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1197 return (ctx->major);
1198 }
1199
1200+ gss_release_buffer(&ctx->minor, &ename);
1201+
1202 /* We can't copy this structure, so we just move the pointer to it */
1203 client->creds = ctx->client_creds;
1204 ctx->client_creds = GSS_C_NO_CREDENTIAL;
1205@@ -356,19 +432,23 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
1206
1207 /* Privileged */
1208 int
1209-ssh_gssapi_userok(char *user)
1210+ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
1211 {
1212 OM_uint32 lmin;
1213
1214+ (void) kex; /* used in privilege separation */
1215+
1216 if (gssapi_client.exportedname.length == 0 ||
1217 gssapi_client.exportedname.value == NULL) {
1218 debug("No suitable client data");
1219 return 0;
1220 }
1221 if (gssapi_client.mech && gssapi_client.mech->userok)
1222- if ((*gssapi_client.mech->userok)(&gssapi_client, user))
1223+ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
1224+ gssapi_client.used = 1;
1225+ gssapi_client.store.owner = pw;
1226 return 1;
1227- else {
1228+ } else {
1229 /* Destroy delegated credentials if userok fails */
1230 gss_release_buffer(&lmin, &gssapi_client.displayname);
1231 gss_release_buffer(&lmin, &gssapi_client.exportedname);
1232@@ -382,14 +462,90 @@ ssh_gssapi_userok(char *user)
1233 return (0);
1234 }
1235
1236-/* Privileged */
1237-OM_uint32
1238-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1239+/* These bits are only used for rekeying. The unpriviledged child is running
1240+ * as the user, the monitor is root.
1241+ *
1242+ * In the child, we want to :
1243+ * *) Ask the monitor to store our credentials into the store we specify
1244+ * *) If it succeeds, maybe do a PAM update
1245+ */
1246+
1247+/* Stuff for PAM */
1248+
1249+#ifdef USE_PAM
1250+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
1251+ struct pam_response **resp, void *data)
1252 {
1253- ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
1254- gssbuf, gssmic, NULL);
1255+ return (PAM_CONV_ERR);
1256+}
1257+#endif
1258
1259- return (ctx->major);
1260+void
1261+ssh_gssapi_rekey_creds(void) {
1262+ int ok;
1263+#ifdef USE_PAM
1264+ int ret;
1265+ pam_handle_t *pamh = NULL;
1266+ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
1267+ char *envstr;
1268+#endif
1269+
1270+ if (gssapi_client.store.filename == NULL &&
1271+ gssapi_client.store.envval == NULL &&
1272+ gssapi_client.store.envvar == NULL)
1273+ return;
1274+
1275+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
1276+
1277+ if (!ok)
1278+ return;
1279+
1280+ debug("Rekeyed credentials stored successfully");
1281+
1282+ /* Actually managing to play with the ssh pam stack from here will
1283+ * be next to impossible. In any case, we may want different options
1284+ * for rekeying. So, use our own :)
1285+ */
1286+#ifdef USE_PAM
1287+ if (!use_privsep) {
1288+ debug("Not even going to try and do PAM with privsep disabled");
1289+ return;
1290+ }
1291+
1292+ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
1293+ &pamconv, &pamh);
1294+ if (ret)
1295+ return;
1296+
1297+ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
1298+ gssapi_client.store.envval);
1299+
1300+ ret = pam_putenv(pamh, envstr);
1301+ if (!ret)
1302+ pam_setcred(pamh, PAM_REINITIALIZE_CRED);
1303+ pam_end(pamh, PAM_SUCCESS);
1304+#endif
1305+}
1306+
1307+int
1308+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
1309+ int ok = 0;
1310+
1311+ /* Check we've got credentials to store */
1312+ if (!gssapi_client.updated)
1313+ return 0;
1314+
1315+ gssapi_client.updated = 0;
1316+
1317+ temporarily_use_uid(gssapi_client.store.owner);
1318+ if (gssapi_client.mech && gssapi_client.mech->updatecreds)
1319+ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
1320+ else
1321+ debug("No update function for this mechanism");
1322+
1323+ restore_uid();
1324+
1325+ return ok;
1326 }
1327
1328 /* Privileged */
1329diff --git a/kex.c b/kex.c
1330index ce85f0439..574c76093 100644
1331--- a/kex.c
1332+++ b/kex.c
1333@@ -57,11 +57,16 @@
1334 #include "misc.h"
1335 #include "dispatch.h"
1336 #include "monitor.h"
1337+#include "xmalloc.h"
1338
1339 #include "ssherr.h"
1340 #include "sshbuf.h"
1341 #include "digest.h"
1342
1343+#ifdef GSSAPI
1344+#include "ssh-gss.h"
1345+#endif
1346+
1347 /* prototype */
1348 static int kex_choose_conf(struct ssh *);
1349 static int kex_input_newkeys(int, u_int32_t, struct ssh *);
1350@@ -115,15 +120,28 @@ static const struct kexalg kexalgs[] = {
1351 #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
1352 { NULL, 0, -1, -1},
1353 };
1354+static const struct kexalg gss_kexalgs[] = {
1355+#ifdef GSSAPI
1356+ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
1357+ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
1358+ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
1359+ { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
1360+ { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
1361+ { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256,
1362+ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
1363+ { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
1364+#endif
1365+ { NULL, 0, -1, -1},
1366+};
1367
1368-char *
1369-kex_alg_list(char sep)
1370+static char *
1371+kex_alg_list_internal(char sep, const struct kexalg *algs)
1372 {
1373 char *ret = NULL, *tmp;
1374 size_t nlen, rlen = 0;
1375 const struct kexalg *k;
1376
1377- for (k = kexalgs; k->name != NULL; k++) {
1378+ for (k = algs; k->name != NULL; k++) {
1379 if (ret != NULL)
1380 ret[rlen++] = sep;
1381 nlen = strlen(k->name);
1382@@ -138,6 +156,18 @@ kex_alg_list(char sep)
1383 return ret;
1384 }
1385
1386+char *
1387+kex_alg_list(char sep)
1388+{
1389+ return kex_alg_list_internal(sep, kexalgs);
1390+}
1391+
1392+char *
1393+kex_gss_alg_list(char sep)
1394+{
1395+ return kex_alg_list_internal(sep, gss_kexalgs);
1396+}
1397+
1398 static const struct kexalg *
1399 kex_alg_by_name(const char *name)
1400 {
1401@@ -147,6 +177,10 @@ kex_alg_by_name(const char *name)
1402 if (strcmp(k->name, name) == 0)
1403 return k;
1404 }
1405+ for (k = gss_kexalgs; k->name != NULL; k++) {
1406+ if (strncmp(k->name, name, strlen(k->name)) == 0)
1407+ return k;
1408+ }
1409 return NULL;
1410 }
1411
1412@@ -315,6 +349,29 @@ kex_assemble_names(char **listp, const char *def, const char *all)
1413 return r;
1414 }
1415
1416+/* Validate GSS KEX method name list */
1417+int
1418+kex_gss_names_valid(const char *names)
1419+{
1420+ char *s, *cp, *p;
1421+
1422+ if (names == NULL || *names == '\0')
1423+ return 0;
1424+ s = cp = xstrdup(names);
1425+ for ((p = strsep(&cp, ",")); p && *p != '\0';
1426+ (p = strsep(&cp, ","))) {
1427+ if (strncmp(p, "gss-", 4) != 0
1428+ || kex_alg_by_name(p) == NULL) {
1429+ error("Unsupported KEX algorithm \"%.100s\"", p);
1430+ free(s);
1431+ return 0;
1432+ }
1433+ }
1434+ debug3("gss kex names ok: [%s]", names);
1435+ free(s);
1436+ return 1;
1437+}
1438+
1439 /* put algorithm proposal into buffer */
1440 int
1441 kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
1442@@ -698,6 +755,9 @@ kex_free(struct kex *kex)
1443 sshbuf_free(kex->server_version);
1444 sshbuf_free(kex->client_pub);
1445 free(kex->session_id);
1446+#ifdef GSSAPI
1447+ free(kex->gss_host);
1448+#endif /* GSSAPI */
1449 free(kex->failed_choice);
1450 free(kex->hostkey_alg);
1451 free(kex->name);
1452diff --git a/kex.h b/kex.h
1453index a5ae6ac05..fe7141414 100644
1454--- a/kex.h
1455+++ b/kex.h
1456@@ -102,6 +102,15 @@ enum kex_exchange {
1457 KEX_ECDH_SHA2,
1458 KEX_C25519_SHA256,
1459 KEX_KEM_SNTRUP4591761X25519_SHA512,
1460+#ifdef GSSAPI
1461+ KEX_GSS_GRP1_SHA1,
1462+ KEX_GSS_GRP14_SHA1,
1463+ KEX_GSS_GRP14_SHA256,
1464+ KEX_GSS_GRP16_SHA512,
1465+ KEX_GSS_GEX_SHA1,
1466+ KEX_GSS_NISTP256_SHA256,
1467+ KEX_GSS_C25519_SHA256,
1468+#endif
1469 KEX_MAX
1470 };
1471
1472@@ -153,6 +162,12 @@ struct kex {
1473 u_int flags;
1474 int hash_alg;
1475 int ec_nid;
1476+#ifdef GSSAPI
1477+ int gss_deleg_creds;
1478+ int gss_trust_dns;
1479+ char *gss_host;
1480+ char *gss_client;
1481+#endif
1482 char *failed_choice;
1483 int (*verify_host_key)(struct sshkey *, struct ssh *);
1484 struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
1485@@ -174,8 +189,10 @@ struct kex {
1486
1487 int kex_names_valid(const char *);
1488 char *kex_alg_list(char);
1489+char *kex_gss_alg_list(char);
1490 char *kex_names_cat(const char *, const char *);
1491 int kex_assemble_names(char **, const char *, const char *);
1492+int kex_gss_names_valid(const char *);
1493
1494 int kex_exchange_identification(struct ssh *, int, const char *);
1495
1496@@ -202,6 +219,12 @@ int kexgex_client(struct ssh *);
1497 int kexgex_server(struct ssh *);
1498 int kex_gen_client(struct ssh *);
1499 int kex_gen_server(struct ssh *);
1500+#if defined(GSSAPI) && defined(WITH_OPENSSL)
1501+int kexgssgex_client(struct ssh *);
1502+int kexgssgex_server(struct ssh *);
1503+int kexgss_client(struct ssh *);
1504+int kexgss_server(struct ssh *);
1505+#endif
1506
1507 int kex_dh_keypair(struct kex *);
1508 int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
1509@@ -234,6 +257,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *,
1510 const BIGNUM *, const u_char *, size_t,
1511 u_char *, size_t *);
1512
1513+int kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
1514+ const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
1515+ const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
1516+ const struct sshbuf *client_pub, const struct sshbuf *server_pub,
1517+ const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
1518+
1519 void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
1520 __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
1521 __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
1522diff --git a/kexdh.c b/kexdh.c
1523index 67133e339..edaa46762 100644
1524--- a/kexdh.c
1525+++ b/kexdh.c
1526@@ -48,13 +48,23 @@ kex_dh_keygen(struct kex *kex)
1527 {
1528 switch (kex->kex_type) {
1529 case KEX_DH_GRP1_SHA1:
1530+#ifdef GSSAPI
1531+ case KEX_GSS_GRP1_SHA1:
1532+#endif
1533 kex->dh = dh_new_group1();
1534 break;
1535 case KEX_DH_GRP14_SHA1:
1536 case KEX_DH_GRP14_SHA256:
1537+#ifdef GSSAPI
1538+ case KEX_GSS_GRP14_SHA1:
1539+ case KEX_GSS_GRP14_SHA256:
1540+#endif
1541 kex->dh = dh_new_group14();
1542 break;
1543 case KEX_DH_GRP16_SHA512:
1544+#ifdef GSSAPI
1545+ case KEX_GSS_GRP16_SHA512:
1546+#endif
1547 kex->dh = dh_new_group16();
1548 break;
1549 case KEX_DH_GRP18_SHA512:
1550diff --git a/kexgen.c b/kexgen.c
1551index 69348b964..c0e8c2f44 100644
1552--- a/kexgen.c
1553+++ b/kexgen.c
1554@@ -44,7 +44,7 @@
1555 static int input_kex_gen_init(int, u_int32_t, struct ssh *);
1556 static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
1557
1558-static int
1559+int
1560 kex_gen_hash(
1561 int hash_alg,
1562 const struct sshbuf *client_version,
1563diff --git a/kexgssc.c b/kexgssc.c
1564new file mode 100644
1565index 000000000..f6e1405eb
1566--- /dev/null
1567+++ b/kexgssc.c
1568@@ -0,0 +1,606 @@
1569+/*
1570+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1571+ *
1572+ * Redistribution and use in source and binary forms, with or without
1573+ * modification, are permitted provided that the following conditions
1574+ * are met:
1575+ * 1. Redistributions of source code must retain the above copyright
1576+ * notice, this list of conditions and the following disclaimer.
1577+ * 2. Redistributions in binary form must reproduce the above copyright
1578+ * notice, this list of conditions and the following disclaimer in the
1579+ * documentation and/or other materials provided with the distribution.
1580+ *
1581+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1582+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1583+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1584+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1585+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1586+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1587+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1588+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1589+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1590+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1591+ */
1592+
1593+#include "includes.h"
1594+
1595+#if defined(GSSAPI) && defined(WITH_OPENSSL)
1596+
1597+#include "includes.h"
1598+
1599+#include <openssl/crypto.h>
1600+#include <openssl/bn.h>
1601+
1602+#include <string.h>
1603+
1604+#include "xmalloc.h"
1605+#include "sshbuf.h"
1606+#include "ssh2.h"
1607+#include "sshkey.h"
1608+#include "cipher.h"
1609+#include "kex.h"
1610+#include "log.h"
1611+#include "packet.h"
1612+#include "dh.h"
1613+#include "digest.h"
1614+#include "ssherr.h"
1615+
1616+#include "ssh-gss.h"
1617+
1618+int
1619+kexgss_client(struct ssh *ssh)
1620+{
1621+ struct kex *kex = ssh->kex;
1622+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
1623+ recv_tok = GSS_C_EMPTY_BUFFER,
1624+ gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
1625+ Gssctxt *ctxt;
1626+ OM_uint32 maj_status, min_status, ret_flags;
1627+ struct sshbuf *server_blob = NULL;
1628+ struct sshbuf *shared_secret = NULL;
1629+ struct sshbuf *server_host_key_blob = NULL;
1630+ struct sshbuf *empty = NULL;
1631+ u_char *msg;
1632+ int type = 0;
1633+ int first = 1;
1634+ u_char hash[SSH_DIGEST_MAX_LENGTH];
1635+ size_t hashlen;
1636+ u_char c;
1637+ int r;
1638+
1639+ /* Initialise our GSSAPI world */
1640+ ssh_gssapi_build_ctx(&ctxt);
1641+ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
1642+ == GSS_C_NO_OID)
1643+ fatal("Couldn't identify host exchange");
1644+
1645+ if (ssh_gssapi_import_name(ctxt, kex->gss_host))
1646+ fatal("Couldn't import hostname");
1647+
1648+ if (kex->gss_client &&
1649+ ssh_gssapi_client_identity(ctxt, kex->gss_client))
1650+ fatal("Couldn't acquire client credentials");
1651+
1652+ /* Step 1 */
1653+ switch (kex->kex_type) {
1654+ case KEX_GSS_GRP1_SHA1:
1655+ case KEX_GSS_GRP14_SHA1:
1656+ case KEX_GSS_GRP14_SHA256:
1657+ case KEX_GSS_GRP16_SHA512:
1658+ r = kex_dh_keypair(kex);
1659+ break;
1660+ case KEX_GSS_NISTP256_SHA256:
1661+ r = kex_ecdh_keypair(kex);
1662+ break;
1663+ case KEX_GSS_C25519_SHA256:
1664+ r = kex_c25519_keypair(kex);
1665+ break;
1666+ default:
1667+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1668+ }
1669+ if (r != 0)
1670+ return r;
1671+
1672+ token_ptr = GSS_C_NO_BUFFER;
1673+
1674+ do {
1675+ debug("Calling gss_init_sec_context");
1676+
1677+ maj_status = ssh_gssapi_init_ctx(ctxt,
1678+ kex->gss_deleg_creds, token_ptr, &send_tok,
1679+ &ret_flags);
1680+
1681+ if (GSS_ERROR(maj_status)) {
1682+ /* XXX Useles code: Missing send? */
1683+ if (send_tok.length != 0) {
1684+ if ((r = sshpkt_start(ssh,
1685+ SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
1686+ (r = sshpkt_put_string(ssh, send_tok.value,
1687+ send_tok.length)) != 0)
1688+ fatal("sshpkt failed: %s", ssh_err(r));
1689+ }
1690+ fatal("gss_init_context failed");
1691+ }
1692+
1693+ /* If we've got an old receive buffer get rid of it */
1694+ if (token_ptr != GSS_C_NO_BUFFER)
1695+ gss_release_buffer(&min_status, &recv_tok);
1696+
1697+ if (maj_status == GSS_S_COMPLETE) {
1698+ /* If mutual state flag is not true, kex fails */
1699+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
1700+ fatal("Mutual authentication failed");
1701+
1702+ /* If integ avail flag is not true kex fails */
1703+ if (!(ret_flags & GSS_C_INTEG_FLAG))
1704+ fatal("Integrity check failed");
1705+ }
1706+
1707+ /*
1708+ * If we have data to send, then the last message that we
1709+ * received cannot have been a 'complete'.
1710+ */
1711+ if (send_tok.length != 0) {
1712+ if (first) {
1713+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
1714+ (r = sshpkt_put_string(ssh, send_tok.value,
1715+ send_tok.length)) != 0 ||
1716+ (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
1717+ fatal("failed to construct packet: %s", ssh_err(r));
1718+ first = 0;
1719+ } else {
1720+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
1721+ (r = sshpkt_put_string(ssh, send_tok.value,
1722+ send_tok.length)) != 0)
1723+ fatal("failed to construct packet: %s", ssh_err(r));
1724+ }
1725+ if ((r = sshpkt_send(ssh)) != 0)
1726+ fatal("failed to send packet: %s", ssh_err(r));
1727+ gss_release_buffer(&min_status, &send_tok);
1728+
1729+ /* If we've sent them data, they should reply */
1730+ do {
1731+ type = ssh_packet_read(ssh);
1732+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
1733+ debug("Received KEXGSS_HOSTKEY");
1734+ if (server_host_key_blob)
1735+ fatal("Server host key received more than once");
1736+ if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
1737+ fatal("Failed to read server host key: %s", ssh_err(r));
1738+ }
1739+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
1740+
1741+ switch (type) {
1742+ case SSH2_MSG_KEXGSS_CONTINUE:
1743+ debug("Received GSSAPI_CONTINUE");
1744+ if (maj_status == GSS_S_COMPLETE)
1745+ fatal("GSSAPI Continue received from server when complete");
1746+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
1747+ &recv_tok)) != 0 ||
1748+ (r = sshpkt_get_end(ssh)) != 0)
1749+ fatal("Failed to read token: %s", ssh_err(r));
1750+ break;
1751+ case SSH2_MSG_KEXGSS_COMPLETE:
1752+ debug("Received GSSAPI_COMPLETE");
1753+ if (msg_tok.value != NULL)
1754+ fatal("Received GSSAPI_COMPLETE twice?");
1755+ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
1756+ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
1757+ &msg_tok)) != 0)
1758+ fatal("Failed to read message: %s", ssh_err(r));
1759+
1760+ /* Is there a token included? */
1761+ if ((r = sshpkt_get_u8(ssh, &c)) != 0)
1762+ fatal("sshpkt failed: %s", ssh_err(r));
1763+ if (c) {
1764+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
1765+ ssh, &recv_tok)) != 0)
1766+ fatal("Failed to read token: %s", ssh_err(r));
1767+ /* If we're already complete - protocol error */
1768+ if (maj_status == GSS_S_COMPLETE)
1769+ sshpkt_disconnect(ssh, "Protocol error: received token when complete");
1770+ } else {
1771+ /* No token included */
1772+ if (maj_status != GSS_S_COMPLETE)
1773+ sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
1774+ }
1775+ if ((r = sshpkt_get_end(ssh)) != 0) {
1776+ fatal("Expecting end of packet.");
1777+ }
1778+ break;
1779+ case SSH2_MSG_KEXGSS_ERROR:
1780+ debug("Received Error");
1781+ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
1782+ (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
1783+ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
1784+ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
1785+ (r = sshpkt_get_end(ssh)) != 0)
1786+ fatal("sshpkt_get failed: %s", ssh_err(r));
1787+ fatal("GSSAPI Error: \n%.400s", msg);
1788+ default:
1789+ sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
1790+ type);
1791+ }
1792+ token_ptr = &recv_tok;
1793+ } else {
1794+ /* No data, and not complete */
1795+ if (maj_status != GSS_S_COMPLETE)
1796+ fatal("Not complete, and no token output");
1797+ }
1798+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
1799+
1800+ /*
1801+ * We _must_ have received a COMPLETE message in reply from the
1802+ * server, which will have set server_blob and msg_tok
1803+ */
1804+
1805+ if (type != SSH2_MSG_KEXGSS_COMPLETE)
1806+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
1807+
1808+ /* compute shared secret */
1809+ switch (kex->kex_type) {
1810+ case KEX_GSS_GRP1_SHA1:
1811+ case KEX_GSS_GRP14_SHA1:
1812+ case KEX_GSS_GRP14_SHA256:
1813+ case KEX_GSS_GRP16_SHA512:
1814+ r = kex_dh_dec(kex, server_blob, &shared_secret);
1815+ break;
1816+ case KEX_GSS_C25519_SHA256:
1817+ if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
1818+ fatal("The received key has MSB of last octet set!");
1819+ r = kex_c25519_dec(kex, server_blob, &shared_secret);
1820+ break;
1821+ case KEX_GSS_NISTP256_SHA256:
1822+ if (sshbuf_len(server_blob) != 65)
1823+ fatal("The received NIST-P256 key did not match"
1824+ "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
1825+
1826+ if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
1827+ fatal("The received NIST-P256 key does not have first octet 0x04");
1828+
1829+ r = kex_ecdh_dec(kex, server_blob, &shared_secret);
1830+ break;
1831+ default:
1832+ r = SSH_ERR_INVALID_ARGUMENT;
1833+ break;
1834+ }
1835+ if (r != 0)
1836+ goto out;
1837+
1838+ if ((empty = sshbuf_new()) == NULL) {
1839+ r = SSH_ERR_ALLOC_FAIL;
1840+ goto out;
1841+ }
1842+
1843+ hashlen = sizeof(hash);
1844+ if ((r = kex_gen_hash(
1845+ kex->hash_alg,
1846+ kex->client_version,
1847+ kex->server_version,
1848+ kex->my,
1849+ kex->peer,
1850+ (server_host_key_blob ? server_host_key_blob : empty),
1851+ kex->client_pub,
1852+ server_blob,
1853+ shared_secret,
1854+ hash, &hashlen)) != 0)
1855+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1856+
1857+ gssbuf.value = hash;
1858+ gssbuf.length = hashlen;
1859+
1860+ /* Verify that the hash matches the MIC we just got. */
1861+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
1862+ sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
1863+
1864+ gss_release_buffer(&min_status, &msg_tok);
1865+
1866+ if (kex->gss_deleg_creds)
1867+ ssh_gssapi_credentials_updated(ctxt);
1868+
1869+ if (gss_kex_context == NULL)
1870+ gss_kex_context = ctxt;
1871+ else
1872+ ssh_gssapi_delete_ctx(&ctxt);
1873+
1874+ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
1875+ r = kex_send_newkeys(ssh);
1876+
1877+out:
1878+ explicit_bzero(hash, sizeof(hash));
1879+ explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
1880+ sshbuf_free(empty);
1881+ sshbuf_free(server_host_key_blob);
1882+ sshbuf_free(server_blob);
1883+ sshbuf_free(shared_secret);
1884+ sshbuf_free(kex->client_pub);
1885+ kex->client_pub = NULL;
1886+ return r;
1887+}
1888+
1889+int
1890+kexgssgex_client(struct ssh *ssh)
1891+{
1892+ struct kex *kex = ssh->kex;
1893+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
1894+ recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
1895+ msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
1896+ Gssctxt *ctxt;
1897+ OM_uint32 maj_status, min_status, ret_flags;
1898+ struct sshbuf *shared_secret = NULL;
1899+ BIGNUM *p = NULL;
1900+ BIGNUM *g = NULL;
1901+ struct sshbuf *buf = NULL;
1902+ struct sshbuf *server_host_key_blob = NULL;
1903+ struct sshbuf *server_blob = NULL;
1904+ BIGNUM *dh_server_pub = NULL;
1905+ u_char *msg;
1906+ int type = 0;
1907+ int first = 1;
1908+ u_char hash[SSH_DIGEST_MAX_LENGTH];
1909+ size_t hashlen;
1910+ const BIGNUM *pub_key, *dh_p, *dh_g;
1911+ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
1912+ struct sshbuf *empty = NULL;
1913+ u_char c;
1914+ int r;
1915+
1916+ /* Initialise our GSSAPI world */
1917+ ssh_gssapi_build_ctx(&ctxt);
1918+ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
1919+ == GSS_C_NO_OID)
1920+ fatal("Couldn't identify host exchange");
1921+
1922+ if (ssh_gssapi_import_name(ctxt, kex->gss_host))
1923+ fatal("Couldn't import hostname");
1924+
1925+ if (kex->gss_client &&
1926+ ssh_gssapi_client_identity(ctxt, kex->gss_client))
1927+ fatal("Couldn't acquire client credentials");
1928+
1929+ debug("Doing group exchange");
1930+ nbits = dh_estimate(kex->dh_need * 8);
1931+
1932+ kex->min = DH_GRP_MIN;
1933+ kex->max = DH_GRP_MAX;
1934+ kex->nbits = nbits;
1935+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
1936+ (r = sshpkt_put_u32(ssh, min)) != 0 ||
1937+ (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
1938+ (r = sshpkt_put_u32(ssh, max)) != 0 ||
1939+ (r = sshpkt_send(ssh)) != 0)
1940+ fatal("Failed to construct a packet: %s", ssh_err(r));
1941+
1942+ if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
1943+ fatal("Error: %s", ssh_err(r));
1944+
1945+ if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
1946+ (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
1947+ (r = sshpkt_get_end(ssh)) != 0)
1948+ fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
1949+
1950+ if (BN_num_bits(p) < min || BN_num_bits(p) > max)
1951+ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
1952+ min, BN_num_bits(p), max);
1953+
1954+ if ((kex->dh = dh_new_group(g, p)) == NULL)
1955+ fatal("dn_new_group() failed");
1956+ p = g = NULL; /* belong to kex->dh now */
1957+
1958+ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
1959+ goto out;
1960+ DH_get0_key(kex->dh, &pub_key, NULL);
1961+
1962+ token_ptr = GSS_C_NO_BUFFER;
1963+
1964+ do {
1965+ /* Step 2 - call GSS_Init_sec_context() */
1966+ debug("Calling gss_init_sec_context");
1967+
1968+ maj_status = ssh_gssapi_init_ctx(ctxt,
1969+ kex->gss_deleg_creds, token_ptr, &send_tok,
1970+ &ret_flags);
1971+
1972+ if (GSS_ERROR(maj_status)) {
1973+ /* XXX Useles code: Missing send? */
1974+ if (send_tok.length != 0) {
1975+ if ((r = sshpkt_start(ssh,
1976+ SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
1977+ (r = sshpkt_put_string(ssh, send_tok.value,
1978+ send_tok.length)) != 0)
1979+ fatal("sshpkt failed: %s", ssh_err(r));
1980+ }
1981+ fatal("gss_init_context failed");
1982+ }
1983+
1984+ /* If we've got an old receive buffer get rid of it */
1985+ if (token_ptr != GSS_C_NO_BUFFER)
1986+ gss_release_buffer(&min_status, &recv_tok);
1987+
1988+ if (maj_status == GSS_S_COMPLETE) {
1989+ /* If mutual state flag is not true, kex fails */
1990+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
1991+ fatal("Mutual authentication failed");
1992+
1993+ /* If integ avail flag is not true kex fails */
1994+ if (!(ret_flags & GSS_C_INTEG_FLAG))
1995+ fatal("Integrity check failed");
1996+ }
1997+
1998+ /*
1999+ * If we have data to send, then the last message that we
2000+ * received cannot have been a 'complete'.
2001+ */
2002+ if (send_tok.length != 0) {
2003+ if (first) {
2004+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
2005+ (r = sshpkt_put_string(ssh, send_tok.value,
2006+ send_tok.length)) != 0 ||
2007+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
2008+ fatal("sshpkt failed: %s", ssh_err(r));
2009+ first = 0;
2010+ } else {
2011+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2012+ (r = sshpkt_put_string(ssh,send_tok.value,
2013+ send_tok.length)) != 0)
2014+ fatal("sshpkt failed: %s", ssh_err(r));
2015+ }
2016+ if ((r = sshpkt_send(ssh)) != 0)
2017+ fatal("sshpkt_send failed: %s", ssh_err(r));
2018+ gss_release_buffer(&min_status, &send_tok);
2019+
2020+ /* If we've sent them data, they should reply */
2021+ do {
2022+ type = ssh_packet_read(ssh);
2023+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
2024+ debug("Received KEXGSS_HOSTKEY");
2025+ if (server_host_key_blob)
2026+ fatal("Server host key received more than once");
2027+ if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
2028+ fatal("sshpkt failed: %s", ssh_err(r));
2029+ }
2030+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
2031+
2032+ switch (type) {
2033+ case SSH2_MSG_KEXGSS_CONTINUE:
2034+ debug("Received GSSAPI_CONTINUE");
2035+ if (maj_status == GSS_S_COMPLETE)
2036+ fatal("GSSAPI Continue received from server when complete");
2037+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2038+ &recv_tok)) != 0 ||
2039+ (r = sshpkt_get_end(ssh)) != 0)
2040+ fatal("sshpkt failed: %s", ssh_err(r));
2041+ break;
2042+ case SSH2_MSG_KEXGSS_COMPLETE:
2043+ debug("Received GSSAPI_COMPLETE");
2044+ if (msg_tok.value != NULL)
2045+ fatal("Received GSSAPI_COMPLETE twice?");
2046+ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
2047+ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2048+ &msg_tok)) != 0)
2049+ fatal("sshpkt failed: %s", ssh_err(r));
2050+
2051+ /* Is there a token included? */
2052+ if ((r = sshpkt_get_u8(ssh, &c)) != 0)
2053+ fatal("sshpkt failed: %s", ssh_err(r));
2054+ if (c) {
2055+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
2056+ ssh, &recv_tok)) != 0 ||
2057+ (r = sshpkt_get_end(ssh)) != 0)
2058+ fatal("sshpkt failed: %s", ssh_err(r));
2059+ /* If we're already complete - protocol error */
2060+ if (maj_status == GSS_S_COMPLETE)
2061+ sshpkt_disconnect(ssh, "Protocol error: received token when complete");
2062+ } else {
2063+ /* No token included */
2064+ if (maj_status != GSS_S_COMPLETE)
2065+ sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
2066+ }
2067+ break;
2068+ case SSH2_MSG_KEXGSS_ERROR:
2069+ debug("Received Error");
2070+ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
2071+ (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
2072+ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
2073+ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
2074+ (r = sshpkt_get_end(ssh)) != 0)
2075+ fatal("sshpkt failed: %s", ssh_err(r));
2076+ fatal("GSSAPI Error: \n%.400s", msg);
2077+ default:
2078+ sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d",
2079+ type);
2080+ }
2081+ token_ptr = &recv_tok;
2082+ } else {
2083+ /* No data, and not complete */
2084+ if (maj_status != GSS_S_COMPLETE)
2085+ fatal("Not complete, and no token output");
2086+ }
2087+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
2088+
2089+ /*
2090+ * We _must_ have received a COMPLETE message in reply from the
2091+ * server, which will have set dh_server_pub and msg_tok
2092+ */
2093+
2094+ if (type != SSH2_MSG_KEXGSS_COMPLETE)
2095+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
2096+
2097+ /* 7. C verifies that the key Q_S is valid */
2098+ /* 8. C computes shared secret */
2099+ if ((buf = sshbuf_new()) == NULL ||
2100+ (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
2101+ (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
2102+ goto out;
2103+ sshbuf_free(buf);
2104+ buf = NULL;
2105+
2106+ if ((shared_secret = sshbuf_new()) == NULL) {
2107+ r = SSH_ERR_ALLOC_FAIL;
2108+ goto out;
2109+ }
2110+
2111+ if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
2112+ goto out;
2113+ if ((empty = sshbuf_new()) == NULL) {
2114+ r = SSH_ERR_ALLOC_FAIL;
2115+ goto out;
2116+ }
2117+
2118+ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
2119+ hashlen = sizeof(hash);
2120+ if ((r = kexgex_hash(
2121+ kex->hash_alg,
2122+ kex->client_version,
2123+ kex->server_version,
2124+ kex->my,
2125+ kex->peer,
2126+ (server_host_key_blob ? server_host_key_blob : empty),
2127+ kex->min, kex->nbits, kex->max,
2128+ dh_p, dh_g,
2129+ pub_key,
2130+ dh_server_pub,
2131+ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
2132+ hash, &hashlen)) != 0)
2133+ fatal("Failed to calculate hash: %s", ssh_err(r));
2134+
2135+ gssbuf.value = hash;
2136+ gssbuf.length = hashlen;
2137+
2138+ /* Verify that the hash matches the MIC we just got. */
2139+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
2140+ sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
2141+
2142+ gss_release_buffer(&min_status, &msg_tok);
2143+
2144+ /* save session id */
2145+ if (kex->session_id == NULL) {
2146+ kex->session_id_len = hashlen;
2147+ kex->session_id = xmalloc(kex->session_id_len);
2148+ memcpy(kex->session_id, hash, kex->session_id_len);
2149+ }
2150+
2151+ if (kex->gss_deleg_creds)
2152+ ssh_gssapi_credentials_updated(ctxt);
2153+
2154+ if (gss_kex_context == NULL)
2155+ gss_kex_context = ctxt;
2156+ else
2157+ ssh_gssapi_delete_ctx(&ctxt);
2158+
2159+ /* Finally derive the keys and send them */
2160+ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
2161+ r = kex_send_newkeys(ssh);
2162+out:
2163+ sshbuf_free(buf);
2164+ sshbuf_free(server_blob);
2165+ sshbuf_free(empty);
2166+ explicit_bzero(hash, sizeof(hash));
2167+ DH_free(kex->dh);
2168+ kex->dh = NULL;
2169+ BN_clear_free(dh_server_pub);
2170+ sshbuf_free(shared_secret);
2171+ sshbuf_free(server_host_key_blob);
2172+ return r;
2173+}
2174+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
2175diff --git a/kexgsss.c b/kexgsss.c
2176new file mode 100644
2177index 000000000..60bc02deb
2178--- /dev/null
2179+++ b/kexgsss.c
2180@@ -0,0 +1,474 @@
2181+/*
2182+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
2183+ *
2184+ * Redistribution and use in source and binary forms, with or without
2185+ * modification, are permitted provided that the following conditions
2186+ * are met:
2187+ * 1. Redistributions of source code must retain the above copyright
2188+ * notice, this list of conditions and the following disclaimer.
2189+ * 2. Redistributions in binary form must reproduce the above copyright
2190+ * notice, this list of conditions and the following disclaimer in the
2191+ * documentation and/or other materials provided with the distribution.
2192+ *
2193+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
2194+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2195+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2196+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2197+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2198+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2199+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2200+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2201+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2202+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2203+ */
2204+
2205+#include "includes.h"
2206+
2207+#if defined(GSSAPI) && defined(WITH_OPENSSL)
2208+
2209+#include <string.h>
2210+
2211+#include <openssl/crypto.h>
2212+#include <openssl/bn.h>
2213+
2214+#include "xmalloc.h"
2215+#include "sshbuf.h"
2216+#include "ssh2.h"
2217+#include "sshkey.h"
2218+#include "cipher.h"
2219+#include "kex.h"
2220+#include "log.h"
2221+#include "packet.h"
2222+#include "dh.h"
2223+#include "ssh-gss.h"
2224+#include "monitor_wrap.h"
2225+#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */
2226+#include "servconf.h"
2227+#include "ssh-gss.h"
2228+#include "digest.h"
2229+#include "ssherr.h"
2230+
2231+extern ServerOptions options;
2232+
2233+int
2234+kexgss_server(struct ssh *ssh)
2235+{
2236+ struct kex *kex = ssh->kex;
2237+ OM_uint32 maj_status, min_status;
2238+
2239+ /*
2240+ * Some GSSAPI implementations use the input value of ret_flags (an
2241+ * output variable) as a means of triggering mechanism specific
2242+ * features. Initializing it to zero avoids inadvertently
2243+ * activating this non-standard behaviour.
2244+ */
2245+
2246+ OM_uint32 ret_flags = 0;
2247+ gss_buffer_desc gssbuf, recv_tok, msg_tok;
2248+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
2249+ Gssctxt *ctxt = NULL;
2250+ struct sshbuf *shared_secret = NULL;
2251+ struct sshbuf *client_pubkey = NULL;
2252+ struct sshbuf *server_pubkey = NULL;
2253+ struct sshbuf *empty = sshbuf_new();
2254+ int type = 0;
2255+ gss_OID oid;
2256+ char *mechs;
2257+ u_char hash[SSH_DIGEST_MAX_LENGTH];
2258+ size_t hashlen;
2259+ int r;
2260+
2261+ /* Initialise GSSAPI */
2262+
2263+ /* If we're rekeying, privsep means that some of the private structures
2264+ * in the GSSAPI code are no longer available. This kludges them back
2265+ * into life
2266+ */
2267+ if (!ssh_gssapi_oid_table_ok()) {
2268+ mechs = ssh_gssapi_server_mechanisms();
2269+ free(mechs);
2270+ }
2271+
2272+ debug2("%s: Identifying %s", __func__, kex->name);
2273+ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
2274+ if (oid == GSS_C_NO_OID)
2275+ fatal("Unknown gssapi mechanism");
2276+
2277+ debug2("%s: Acquiring credentials", __func__);
2278+
2279+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
2280+ fatal("Unable to acquire credentials for the server");
2281+
2282+ do {
2283+ debug("Wait SSH2_MSG_KEXGSS_INIT");
2284+ type = ssh_packet_read(ssh);
2285+ switch(type) {
2286+ case SSH2_MSG_KEXGSS_INIT:
2287+ if (client_pubkey != NULL)
2288+ fatal("Received KEXGSS_INIT after initialising");
2289+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2290+ &recv_tok)) != 0 ||
2291+ (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
2292+ (r = sshpkt_get_end(ssh)) != 0)
2293+ fatal("sshpkt failed: %s", ssh_err(r));
2294+
2295+ switch (kex->kex_type) {
2296+ case KEX_GSS_GRP1_SHA1:
2297+ case KEX_GSS_GRP14_SHA1:
2298+ case KEX_GSS_GRP14_SHA256:
2299+ case KEX_GSS_GRP16_SHA512:
2300+ r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
2301+ &shared_secret);
2302+ break;
2303+ case KEX_GSS_NISTP256_SHA256:
2304+ r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
2305+ &shared_secret);
2306+ break;
2307+ case KEX_GSS_C25519_SHA256:
2308+ r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
2309+ &shared_secret);
2310+ break;
2311+ default:
2312+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
2313+ }
2314+ if (r != 0)
2315+ goto out;
2316+
2317+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
2318+ break;
2319+ case SSH2_MSG_KEXGSS_CONTINUE:
2320+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2321+ &recv_tok)) != 0 ||
2322+ (r = sshpkt_get_end(ssh)) != 0)
2323+ fatal("sshpkt failed: %s", ssh_err(r));
2324+ break;
2325+ default:
2326+ sshpkt_disconnect(ssh,
2327+ "Protocol error: didn't expect packet type %d",
2328+ type);
2329+ }
2330+
2331+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
2332+ &send_tok, &ret_flags));
2333+
2334+ gss_release_buffer(&min_status, &recv_tok);
2335+
2336+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
2337+ fatal("Zero length token output when incomplete");
2338+
2339+ if (client_pubkey == NULL)
2340+ fatal("No client public key");
2341+
2342+ if (maj_status & GSS_S_CONTINUE_NEEDED) {
2343+ debug("Sending GSSAPI_CONTINUE");
2344+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2345+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
2346+ (r = sshpkt_send(ssh)) != 0)
2347+ fatal("sshpkt failed: %s", ssh_err(r));
2348+ gss_release_buffer(&min_status, &send_tok);
2349+ }
2350+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
2351+
2352+ if (GSS_ERROR(maj_status)) {
2353+ if (send_tok.length > 0) {
2354+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2355+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
2356+ (r = sshpkt_send(ssh)) != 0)
2357+ fatal("sshpkt failed: %s", ssh_err(r));
2358+ }
2359+ fatal("accept_ctx died");
2360+ }
2361+
2362+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
2363+ fatal("Mutual Authentication flag wasn't set");
2364+
2365+ if (!(ret_flags & GSS_C_INTEG_FLAG))
2366+ fatal("Integrity flag wasn't set");
2367+
2368+ hashlen = sizeof(hash);
2369+ if ((r = kex_gen_hash(
2370+ kex->hash_alg,
2371+ kex->client_version,
2372+ kex->server_version,
2373+ kex->peer,
2374+ kex->my,
2375+ empty,
2376+ client_pubkey,
2377+ server_pubkey,
2378+ shared_secret,
2379+ hash, &hashlen)) != 0)
2380+ goto out;
2381+
2382+ gssbuf.value = hash;
2383+ gssbuf.length = hashlen;
2384+
2385+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
2386+ fatal("Couldn't get MIC");
2387+
2388+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
2389+ (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
2390+ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
2391+ fatal("sshpkt failed: %s", ssh_err(r));
2392+
2393+ if (send_tok.length != 0) {
2394+ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
2395+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
2396+ fatal("sshpkt failed: %s", ssh_err(r));
2397+ } else {
2398+ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
2399+ fatal("sshpkt failed: %s", ssh_err(r));
2400+ }
2401+ if ((r = sshpkt_send(ssh)) != 0)
2402+ fatal("sshpkt_send failed: %s", ssh_err(r));
2403+
2404+ gss_release_buffer(&min_status, &send_tok);
2405+ gss_release_buffer(&min_status, &msg_tok);
2406+
2407+ if (gss_kex_context == NULL)
2408+ gss_kex_context = ctxt;
2409+ else
2410+ ssh_gssapi_delete_ctx(&ctxt);
2411+
2412+ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
2413+ r = kex_send_newkeys(ssh);
2414+
2415+ /* If this was a rekey, then save out any delegated credentials we
2416+ * just exchanged. */
2417+ if (options.gss_store_rekey)
2418+ ssh_gssapi_rekey_creds();
2419+out:
2420+ sshbuf_free(empty);
2421+ explicit_bzero(hash, sizeof(hash));
2422+ sshbuf_free(shared_secret);
2423+ sshbuf_free(client_pubkey);
2424+ sshbuf_free(server_pubkey);
2425+ return r;
2426+}
2427+
2428+int
2429+kexgssgex_server(struct ssh *ssh)
2430+{
2431+ struct kex *kex = ssh->kex;
2432+ OM_uint32 maj_status, min_status;
2433+
2434+ /*
2435+ * Some GSSAPI implementations use the input value of ret_flags (an
2436+ * output variable) as a means of triggering mechanism specific
2437+ * features. Initializing it to zero avoids inadvertently
2438+ * activating this non-standard behaviour.
2439+ */
2440+
2441+ OM_uint32 ret_flags = 0;
2442+ gss_buffer_desc gssbuf, recv_tok, msg_tok;
2443+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
2444+ Gssctxt *ctxt = NULL;
2445+ struct sshbuf *shared_secret = NULL;
2446+ int type = 0;
2447+ gss_OID oid;
2448+ char *mechs;
2449+ u_char hash[SSH_DIGEST_MAX_LENGTH];
2450+ size_t hashlen;
2451+ BIGNUM *dh_client_pub = NULL;
2452+ const BIGNUM *pub_key, *dh_p, *dh_g;
2453+ int min = -1, max = -1, nbits = -1;
2454+ int cmin = -1, cmax = -1; /* client proposal */
2455+ struct sshbuf *empty = sshbuf_new();
2456+ int r;
2457+
2458+ /* Initialise GSSAPI */
2459+
2460+ /* If we're rekeying, privsep means that some of the private structures
2461+ * in the GSSAPI code are no longer available. This kludges them back
2462+ * into life
2463+ */
2464+ if (!ssh_gssapi_oid_table_ok())
2465+ if ((mechs = ssh_gssapi_server_mechanisms()))
2466+ free(mechs);
2467+
2468+ debug2("%s: Identifying %s", __func__, kex->name);
2469+ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
2470+ if (oid == GSS_C_NO_OID)
2471+ fatal("Unknown gssapi mechanism");
2472+
2473+ debug2("%s: Acquiring credentials", __func__);
2474+
2475+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
2476+ fatal("Unable to acquire credentials for the server");
2477+
2478+ /* 5. S generates an ephemeral key pair (do the allocations early) */
2479+ debug("Doing group exchange");
2480+ ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
2481+ /* store client proposal to provide valid signature */
2482+ if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
2483+ (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
2484+ (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
2485+ (r = sshpkt_get_end(ssh)) != 0)
2486+ fatal("sshpkt failed: %s", ssh_err(r));
2487+ kex->nbits = nbits;
2488+ kex->min = cmin;
2489+ kex->max = cmax;
2490+ min = MAX(DH_GRP_MIN, cmin);
2491+ max = MIN(DH_GRP_MAX, cmax);
2492+ nbits = MAXIMUM(DH_GRP_MIN, nbits);
2493+ nbits = MINIMUM(DH_GRP_MAX, nbits);
2494+ if (max < min || nbits < min || max < nbits)
2495+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
2496+ min, nbits, max);
2497+ kex->dh = PRIVSEP(choose_dh(min, nbits, max));
2498+ if (kex->dh == NULL) {
2499+ sshpkt_disconnect(ssh, "Protocol error: no matching group found");
2500+ fatal("Protocol error: no matching group found");
2501+ }
2502+
2503+ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
2504+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
2505+ (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
2506+ (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
2507+ (r = sshpkt_send(ssh)) != 0)
2508+ fatal("sshpkt failed: %s", ssh_err(r));
2509+
2510+ if ((r = ssh_packet_write_wait(ssh)) != 0)
2511+ fatal("ssh_packet_write_wait: %s", ssh_err(r));
2512+
2513+ /* Compute our exchange value in parallel with the client */
2514+ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
2515+ goto out;
2516+
2517+ do {
2518+ debug("Wait SSH2_MSG_GSSAPI_INIT");
2519+ type = ssh_packet_read(ssh);
2520+ switch(type) {
2521+ case SSH2_MSG_KEXGSS_INIT:
2522+ if (dh_client_pub != NULL)
2523+ fatal("Received KEXGSS_INIT after initialising");
2524+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2525+ &recv_tok)) != 0 ||
2526+ (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
2527+ (r = sshpkt_get_end(ssh)) != 0)
2528+ fatal("sshpkt failed: %s", ssh_err(r));
2529+
2530+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
2531+ break;
2532+ case SSH2_MSG_KEXGSS_CONTINUE:
2533+ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
2534+ &recv_tok)) != 0 ||
2535+ (r = sshpkt_get_end(ssh)) != 0)
2536+ fatal("sshpkt failed: %s", ssh_err(r));
2537+ break;
2538+ default:
2539+ sshpkt_disconnect(ssh,
2540+ "Protocol error: didn't expect packet type %d",
2541+ type);
2542+ }
2543+
2544+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
2545+ &send_tok, &ret_flags));
2546+
2547+ gss_release_buffer(&min_status, &recv_tok);
2548+
2549+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
2550+ fatal("Zero length token output when incomplete");
2551+
2552+ if (dh_client_pub == NULL)
2553+ fatal("No client public key");
2554+
2555+ if (maj_status & GSS_S_CONTINUE_NEEDED) {
2556+ debug("Sending GSSAPI_CONTINUE");
2557+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2558+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
2559+ (r = sshpkt_send(ssh)) != 0)
2560+ fatal("sshpkt failed: %s", ssh_err(r));
2561+ gss_release_buffer(&min_status, &send_tok);
2562+ }
2563+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
2564+
2565+ if (GSS_ERROR(maj_status)) {
2566+ if (send_tok.length > 0) {
2567+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
2568+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
2569+ (r = sshpkt_send(ssh)) != 0)
2570+ fatal("sshpkt failed: %s", ssh_err(r));
2571+ }
2572+ fatal("accept_ctx died");
2573+ }
2574+
2575+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
2576+ fatal("Mutual Authentication flag wasn't set");
2577+
2578+ if (!(ret_flags & GSS_C_INTEG_FLAG))
2579+ fatal("Integrity flag wasn't set");
2580+
2581+ /* calculate shared secret */
2582+ if ((shared_secret = sshbuf_new()) == NULL) {
2583+ r = SSH_ERR_ALLOC_FAIL;
2584+ goto out;
2585+ }
2586+ if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
2587+ goto out;
2588+
2589+ DH_get0_key(kex->dh, &pub_key, NULL);
2590+ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
2591+ hashlen = sizeof(hash);
2592+ if ((r = kexgex_hash(
2593+ kex->hash_alg,
2594+ kex->client_version,
2595+ kex->server_version,
2596+ kex->peer,
2597+ kex->my,
2598+ empty,
2599+ cmin, nbits, cmax,
2600+ dh_p, dh_g,
2601+ dh_client_pub,
2602+ pub_key,
2603+ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
2604+ hash, &hashlen)) != 0)
2605+ fatal("kexgex_hash failed: %s", ssh_err(r));
2606+
2607+ gssbuf.value = hash;
2608+ gssbuf.length = hashlen;
2609+
2610+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
2611+ fatal("Couldn't get MIC");
2612+
2613+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
2614+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
2615+ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
2616+ fatal("sshpkt failed: %s", ssh_err(r));
2617+
2618+ if (send_tok.length != 0) {
2619+ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
2620+ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
2621+ fatal("sshpkt failed: %s", ssh_err(r));
2622+ } else {
2623+ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
2624+ fatal("sshpkt failed: %s", ssh_err(r));
2625+ }
2626+ if ((r = sshpkt_send(ssh)) != 0)
2627+ fatal("sshpkt failed: %s", ssh_err(r));
2628+
2629+ gss_release_buffer(&min_status, &send_tok);
2630+ gss_release_buffer(&min_status, &msg_tok);
2631+
2632+ if (gss_kex_context == NULL)
2633+ gss_kex_context = ctxt;
2634+ else
2635+ ssh_gssapi_delete_ctx(&ctxt);
2636+
2637+ /* Finally derive the keys and send them */
2638+ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
2639+ r = kex_send_newkeys(ssh);
2640+
2641+ /* If this was a rekey, then save out any delegated credentials we
2642+ * just exchanged. */
2643+ if (options.gss_store_rekey)
2644+ ssh_gssapi_rekey_creds();
2645+out:
2646+ sshbuf_free(empty);
2647+ explicit_bzero(hash, sizeof(hash));
2648+ DH_free(kex->dh);
2649+ kex->dh = NULL;
2650+ BN_clear_free(dh_client_pub);
2651+ sshbuf_free(shared_secret);
2652+ return r;
2653+}
2654+#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
2655diff --git a/monitor.c b/monitor.c
2656index 2ce89fe90..ebf76c7f9 100644
2657--- a/monitor.c
2658+++ b/monitor.c
2659@@ -148,6 +148,8 @@ int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *);
2660 int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *);
2661 int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *);
2662 int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *);
2663+int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *);
2664+int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *);
2665 #endif
2666
2667 #ifdef SSH_AUDIT_EVENTS
2668@@ -220,11 +222,18 @@ struct mon_table mon_dispatch_proto20[] = {
2669 {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
2670 {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok},
2671 {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic},
2672+ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
2673 #endif
2674 {0, 0, NULL}
2675 };
2676
2677 struct mon_table mon_dispatch_postauth20[] = {
2678+#ifdef GSSAPI
2679+ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
2680+ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
2681+ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
2682+ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
2683+#endif
2684 #ifdef WITH_OPENSSL
2685 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
2686 #endif
2687@@ -293,6 +302,10 @@ monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor)
2688 /* Permit requests for moduli and signatures */
2689 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
2690 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
2691+#ifdef GSSAPI
2692+ /* and for the GSSAPI key exchange */
2693+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
2694+#endif
2695
2696 /* The first few requests do not require asynchronous access */
2697 while (!authenticated) {
2698@@ -406,6 +419,10 @@ monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor)
2699 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
2700 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
2701 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
2702+#ifdef GSSAPI
2703+ /* and for the GSSAPI key exchange */
2704+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
2705+#endif
2706
2707 if (auth_opts->permit_pty_flag) {
2708 monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
2709@@ -1713,6 +1730,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
2710 # ifdef OPENSSL_HAS_ECC
2711 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
2712 # endif
2713+# ifdef GSSAPI
2714+ if (options.gss_keyex) {
2715+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2716+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2717+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
2718+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
2719+ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
2720+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
2721+ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
2722+ }
2723+# endif
2724 #endif /* WITH_OPENSSL */
2725 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
2726 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
2727@@ -1806,8 +1834,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
2728 u_char *p;
2729 int r;
2730
2731- if (!options.gss_authentication)
2732- fatal("%s: GSSAPI authentication not enabled", __func__);
2733+ if (!options.gss_authentication && !options.gss_keyex)
2734+ fatal("%s: GSSAPI not enabled", __func__);
2735
2736 if ((r = sshbuf_get_string(m, &p, &len)) != 0)
2737 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2738@@ -1839,8 +1867,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
2739 OM_uint32 flags = 0; /* GSI needs this */
2740 int r;
2741
2742- if (!options.gss_authentication)
2743- fatal("%s: GSSAPI authentication not enabled", __func__);
2744+ if (!options.gss_authentication && !options.gss_keyex)
2745+ fatal("%s: GSSAPI not enabled", __func__);
2746
2747 if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
2748 fatal("%s: buffer error: %s", __func__, ssh_err(r));
2749@@ -1860,6 +1888,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
2750 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2751 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2752 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2753+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2754 }
2755 return (0);
2756 }
2757@@ -1871,8 +1900,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
2758 OM_uint32 ret;
2759 int r;
2760
2761- if (!options.gss_authentication)
2762- fatal("%s: GSSAPI authentication not enabled", __func__);
2763+ if (!options.gss_authentication && !options.gss_keyex)
2764+ fatal("%s: GSSAPI not enabled", __func__);
2765
2766 if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
2767 (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
2768@@ -1898,13 +1927,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
2769 int
2770 mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
2771 {
2772- int r, authenticated;
2773+ int r, authenticated, kex;
2774 const char *displayname;
2775
2776- if (!options.gss_authentication)
2777- fatal("%s: GSSAPI authentication not enabled", __func__);
2778+ if (!options.gss_authentication && !options.gss_keyex)
2779+ fatal("%s: GSSAPI not enabled", __func__);
2780
2781- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
2782+ if ((r = sshbuf_get_u32(m, &kex)) != 0)
2783+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2784+
2785+ authenticated = authctxt->valid &&
2786+ ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
2787
2788 sshbuf_reset(m);
2789 if ((r = sshbuf_put_u32(m, authenticated)) != 0)
2790@@ -1913,7 +1946,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
2791 debug3("%s: sending result %d", __func__, authenticated);
2792 mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
2793
2794- auth_method = "gssapi-with-mic";
2795+ if (kex) {
2796+ auth_method = "gssapi-keyex";
2797+ } else {
2798+ auth_method = "gssapi-with-mic";
2799+ }
2800
2801 if ((displayname = ssh_gssapi_displayname()) != NULL)
2802 auth2_record_info(authctxt, "%s", displayname);
2803@@ -1921,5 +1958,85 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
2804 /* Monitor loop will terminate if authenticated */
2805 return (authenticated);
2806 }
2807+
2808+int
2809+mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
2810+{
2811+ gss_buffer_desc data;
2812+ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2813+ OM_uint32 major, minor;
2814+ size_t len;
2815+ u_char *p = NULL;
2816+ int r;
2817+
2818+ if (!options.gss_authentication && !options.gss_keyex)
2819+ fatal("%s: GSSAPI not enabled", __func__);
2820+
2821+ if ((r = sshbuf_get_string(m, &p, &len)) != 0)
2822+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2823+ data.value = p;
2824+ data.length = len;
2825+ /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
2826+ if (data.length != 20 && data.length != 32 && data.length != 64)
2827+ fatal("%s: data length incorrect: %d", __func__,
2828+ (int) data.length);
2829+
2830+ /* Save the session ID on the first time around */
2831+ if (session_id2_len == 0) {
2832+ session_id2_len = data.length;
2833+ session_id2 = xmalloc(session_id2_len);
2834+ memcpy(session_id2, data.value, session_id2_len);
2835+ }
2836+ major = ssh_gssapi_sign(gsscontext, &data, &hash);
2837+
2838+ free(data.value);
2839+
2840+ sshbuf_reset(m);
2841+
2842+ if ((r = sshbuf_put_u32(m, major)) != 0 ||
2843+ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0)
2844+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2845+
2846+ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2847+
2848+ gss_release_buffer(&minor, &hash);
2849+
2850+ /* Turn on getpwnam permissions */
2851+ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2852+
2853+ /* And credential updating, for when rekeying */
2854+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2855+
2856+ return (0);
2857+}
2858+
2859+int
2860+mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) {
2861+ ssh_gssapi_ccache store;
2862+ int r, ok;
2863+
2864+ if (!options.gss_authentication && !options.gss_keyex)
2865+ fatal("%s: GSSAPI not enabled", __func__);
2866+
2867+ if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
2868+ (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
2869+ (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
2870+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2871+
2872+ ok = ssh_gssapi_update_creds(&store);
2873+
2874+ free(store.filename);
2875+ free(store.envvar);
2876+ free(store.envval);
2877+
2878+ sshbuf_reset(m);
2879+ if ((r = sshbuf_put_u32(m, ok)) != 0)
2880+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2881+
2882+ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2883+
2884+ return(0);
2885+}
2886+
2887 #endif /* GSSAPI */
2888
2889diff --git a/monitor.h b/monitor.h
2890index 683e5e071..2b1a2d590 100644
2891--- a/monitor.h
2892+++ b/monitor.h
2893@@ -63,6 +63,8 @@ enum monitor_reqtype {
2894 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
2895 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
2896
2897+ MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
2898+ MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
2899 };
2900
2901 struct ssh;
2902diff --git a/monitor_wrap.c b/monitor_wrap.c
2903index 001a8fa1c..6edb509a3 100644
2904--- a/monitor_wrap.c
2905+++ b/monitor_wrap.c
2906@@ -993,13 +993,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
2907 }
2908
2909 int
2910-mm_ssh_gssapi_userok(char *user)
2911+mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
2912 {
2913 struct sshbuf *m;
2914 int r, authenticated = 0;
2915
2916 if ((m = sshbuf_new()) == NULL)
2917 fatal("%s: sshbuf_new failed", __func__);
2918+ if ((r = sshbuf_put_u32(m, kex)) != 0)
2919+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2920
2921 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m);
2922 mm_request_receive_expect(pmonitor->m_recvfd,
2923@@ -1012,4 +1014,57 @@ mm_ssh_gssapi_userok(char *user)
2924 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
2925 return (authenticated);
2926 }
2927+
2928+OM_uint32
2929+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
2930+{
2931+ struct sshbuf *m;
2932+ OM_uint32 major;
2933+ int r;
2934+
2935+ if ((m = sshbuf_new()) == NULL)
2936+ fatal("%s: sshbuf_new failed", __func__);
2937+ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
2938+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2939+
2940+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m);
2941+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m);
2942+
2943+ if ((r = sshbuf_get_u32(m, &major)) != 0 ||
2944+ (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
2945+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2946+
2947+ sshbuf_free(m);
2948+
2949+ return (major);
2950+}
2951+
2952+int
2953+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
2954+{
2955+ struct sshbuf *m;
2956+ int r, ok;
2957+
2958+ if ((m = sshbuf_new()) == NULL)
2959+ fatal("%s: sshbuf_new failed", __func__);
2960+
2961+ if ((r = sshbuf_put_cstring(m,
2962+ store->filename ? store->filename : "")) != 0 ||
2963+ (r = sshbuf_put_cstring(m,
2964+ store->envvar ? store->envvar : "")) != 0 ||
2965+ (r = sshbuf_put_cstring(m,
2966+ store->envval ? store->envval : "")) != 0)
2967+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2968+
2969+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m);
2970+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m);
2971+
2972+ if ((r = sshbuf_get_u32(m, &ok)) != 0)
2973+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
2974+
2975+ sshbuf_free(m);
2976+
2977+ return (ok);
2978+}
2979+
2980 #endif /* GSSAPI */
2981diff --git a/monitor_wrap.h b/monitor_wrap.h
2982index 23ab096aa..485590c18 100644
2983--- a/monitor_wrap.h
2984+++ b/monitor_wrap.h
2985@@ -64,8 +64,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
2986 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
2987 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
2988 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
2989-int mm_ssh_gssapi_userok(char *user);
2990+int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex);
2991 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
2992+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
2993+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
2994 #endif
2995
2996 #ifdef USE_PAM
2997diff --git a/readconf.c b/readconf.c
2998index f3cac6b3a..da8022dd0 100644
2999--- a/readconf.c
3000+++ b/readconf.c
3001@@ -67,6 +67,7 @@
3002 #include "uidswap.h"
3003 #include "myproposal.h"
3004 #include "digest.h"
3005+#include "ssh-gss.h"
3006
3007 /* Format of the configuration file:
3008
3009@@ -160,6 +161,8 @@ typedef enum {
3010 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
3011 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
3012 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
3013+ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
3014+ oGssServerIdentity, oGssKexAlgorithms,
3015 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
3016 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
3017 oHashKnownHosts,
3018@@ -204,10 +207,22 @@ static struct {
3019 /* Sometimes-unsupported options */
3020 #if defined(GSSAPI)
3021 { "gssapiauthentication", oGssAuthentication },
3022+ { "gssapikeyexchange", oGssKeyEx },
3023 { "gssapidelegatecredentials", oGssDelegateCreds },
3024+ { "gssapitrustdns", oGssTrustDns },
3025+ { "gssapiclientidentity", oGssClientIdentity },
3026+ { "gssapiserveridentity", oGssServerIdentity },
3027+ { "gssapirenewalforcesrekey", oGssRenewalRekey },
3028+ { "gssapikexalgorithms", oGssKexAlgorithms },
3029 # else
3030 { "gssapiauthentication", oUnsupported },
3031+ { "gssapikeyexchange", oUnsupported },
3032 { "gssapidelegatecredentials", oUnsupported },
3033+ { "gssapitrustdns", oUnsupported },
3034+ { "gssapiclientidentity", oUnsupported },
3035+ { "gssapiserveridentity", oUnsupported },
3036+ { "gssapirenewalforcesrekey", oUnsupported },
3037+ { "gssapikexalgorithms", oUnsupported },
3038 #endif
3039 #ifdef ENABLE_PKCS11
3040 { "pkcs11provider", oPKCS11Provider },
3041@@ -1029,10 +1044,42 @@ parse_time:
3042 intptr = &options->gss_authentication;
3043 goto parse_flag;
3044
3045+ case oGssKeyEx:
3046+ intptr = &options->gss_keyex;
3047+ goto parse_flag;
3048+
3049 case oGssDelegateCreds:
3050 intptr = &options->gss_deleg_creds;
3051 goto parse_flag;
3052
3053+ case oGssTrustDns:
3054+ intptr = &options->gss_trust_dns;
3055+ goto parse_flag;
3056+
3057+ case oGssClientIdentity:
3058+ charptr = &options->gss_client_identity;
3059+ goto parse_string;
3060+
3061+ case oGssServerIdentity:
3062+ charptr = &options->gss_server_identity;
3063+ goto parse_string;
3064+
3065+ case oGssRenewalRekey:
3066+ intptr = &options->gss_renewal_rekey;
3067+ goto parse_flag;
3068+
3069+ case oGssKexAlgorithms:
3070+ arg = strdelim(&s);
3071+ if (!arg || *arg == '\0')
3072+ fatal("%.200s line %d: Missing argument.",
3073+ filename, linenum);
3074+ if (!kex_gss_names_valid(arg))
3075+ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
3076+ filename, linenum, arg ? arg : "<NONE>");
3077+ if (*activep && options->gss_kex_algorithms == NULL)
3078+ options->gss_kex_algorithms = xstrdup(arg);
3079+ break;
3080+
3081 case oBatchMode:
3082 intptr = &options->batch_mode;
3083 goto parse_flag;
3084@@ -1911,7 +1958,13 @@ initialize_options(Options * options)
3085 options->pubkey_authentication = -1;
3086 options->challenge_response_authentication = -1;
3087 options->gss_authentication = -1;
3088+ options->gss_keyex = -1;
3089 options->gss_deleg_creds = -1;
3090+ options->gss_trust_dns = -1;
3091+ options->gss_renewal_rekey = -1;
3092+ options->gss_client_identity = NULL;
3093+ options->gss_server_identity = NULL;
3094+ options->gss_kex_algorithms = NULL;
3095 options->password_authentication = -1;
3096 options->kbd_interactive_authentication = -1;
3097 options->kbd_interactive_devices = NULL;
3098@@ -2059,8 +2112,18 @@ fill_default_options(Options * options)
3099 options->challenge_response_authentication = 1;
3100 if (options->gss_authentication == -1)
3101 options->gss_authentication = 0;
3102+ if (options->gss_keyex == -1)
3103+ options->gss_keyex = 0;
3104 if (options->gss_deleg_creds == -1)
3105 options->gss_deleg_creds = 0;
3106+ if (options->gss_trust_dns == -1)
3107+ options->gss_trust_dns = 0;
3108+ if (options->gss_renewal_rekey == -1)
3109+ options->gss_renewal_rekey = 0;
3110+#ifdef GSSAPI
3111+ if (options->gss_kex_algorithms == NULL)
3112+ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
3113+#endif
3114 if (options->password_authentication == -1)
3115 options->password_authentication = 1;
3116 if (options->kbd_interactive_authentication == -1)
3117@@ -2702,7 +2765,14 @@ dump_client_config(Options *o, const char *host)
3118 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
3119 #ifdef GSSAPI
3120 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
3121+ dump_cfg_fmtint(oGssKeyEx, o->gss_keyex);
3122 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
3123+ dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns);
3124+ dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
3125+ dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
3126+ dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
3127+ dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ?
3128+ o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX);
3129 #endif /* GSSAPI */
3130 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
3131 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
3132diff --git a/readconf.h b/readconf.h
3133index feedb3d20..a8a8870d7 100644
3134--- a/readconf.h
3135+++ b/readconf.h
3136@@ -41,7 +41,13 @@ typedef struct {
3137 int challenge_response_authentication;
3138 /* Try S/Key or TIS, authentication. */
3139 int gss_authentication; /* Try GSS authentication */
3140+ int gss_keyex; /* Try GSS key exchange */
3141 int gss_deleg_creds; /* Delegate GSS credentials */
3142+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */
3143+ int gss_renewal_rekey; /* Credential renewal forces rekey */
3144+ char *gss_client_identity; /* Principal to initiate GSSAPI with */
3145+ char *gss_server_identity; /* GSSAPI target principal */
3146+ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
3147 int password_authentication; /* Try password
3148 * authentication. */
3149 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
3150diff --git a/servconf.c b/servconf.c
3151index 70f5f73f0..191575a16 100644
3152--- a/servconf.c
3153+++ b/servconf.c
3154@@ -69,6 +69,7 @@
3155 #include "auth.h"
3156 #include "myproposal.h"
3157 #include "digest.h"
3158+#include "ssh-gss.h"
3159
3160 static void add_listen_addr(ServerOptions *, const char *,
3161 const char *, int);
3162@@ -133,8 +134,11 @@ initialize_server_options(ServerOptions *options)
3163 options->kerberos_ticket_cleanup = -1;
3164 options->kerberos_get_afs_token = -1;
3165 options->gss_authentication=-1;
3166+ options->gss_keyex = -1;
3167 options->gss_cleanup_creds = -1;
3168 options->gss_strict_acceptor = -1;
3169+ options->gss_store_rekey = -1;
3170+ options->gss_kex_algorithms = NULL;
3171 options->password_authentication = -1;
3172 options->kbd_interactive_authentication = -1;
3173 options->challenge_response_authentication = -1;
3174@@ -375,10 +379,18 @@ fill_default_server_options(ServerOptions *options)
3175 options->kerberos_get_afs_token = 0;
3176 if (options->gss_authentication == -1)
3177 options->gss_authentication = 0;
3178+ if (options->gss_keyex == -1)
3179+ options->gss_keyex = 0;
3180 if (options->gss_cleanup_creds == -1)
3181 options->gss_cleanup_creds = 1;
3182 if (options->gss_strict_acceptor == -1)
3183 options->gss_strict_acceptor = 1;
3184+ if (options->gss_store_rekey == -1)
3185+ options->gss_store_rekey = 0;
3186+#ifdef GSSAPI
3187+ if (options->gss_kex_algorithms == NULL)
3188+ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX);
3189+#endif
3190 if (options->password_authentication == -1)
3191 options->password_authentication = 1;
3192 if (options->kbd_interactive_authentication == -1)
3193@@ -531,6 +543,7 @@ typedef enum {
3194 sHostKeyAlgorithms,
3195 sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
3196 sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
3197+ sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
3198 sAcceptEnv, sSetEnv, sPermitTunnel,
3199 sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
3200 sUsePrivilegeSeparation, sAllowAgentForwarding,
3201@@ -607,12 +620,22 @@ static struct {
3202 #ifdef GSSAPI
3203 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
3204 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
3205+ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
3206 { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
3207+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
3208+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
3209+ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
3210 #else
3211 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
3212 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
3213+ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
3214 { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
3215+ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
3216+ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
3217+ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL },
3218 #endif
3219+ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
3220+ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
3221 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
3222 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
3223 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
3224@@ -1548,6 +1571,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
3225 intptr = &options->gss_authentication;
3226 goto parse_flag;
3227
3228+ case sGssKeyEx:
3229+ intptr = &options->gss_keyex;
3230+ goto parse_flag;
3231+
3232 case sGssCleanupCreds:
3233 intptr = &options->gss_cleanup_creds;
3234 goto parse_flag;
3235@@ -1556,6 +1583,22 @@ process_server_config_line_depth(ServerOptions *options, char *line,
3236 intptr = &options->gss_strict_acceptor;
3237 goto parse_flag;
3238
3239+ case sGssStoreRekey:
3240+ intptr = &options->gss_store_rekey;
3241+ goto parse_flag;
3242+
3243+ case sGssKexAlgorithms:
3244+ arg = strdelim(&cp);
3245+ if (!arg || *arg == '\0')
3246+ fatal("%.200s line %d: Missing argument.",
3247+ filename, linenum);
3248+ if (!kex_gss_names_valid(arg))
3249+ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
3250+ filename, linenum, arg ? arg : "<NONE>");
3251+ if (*activep && options->gss_kex_algorithms == NULL)
3252+ options->gss_kex_algorithms = xstrdup(arg);
3253+ break;
3254+
3255 case sPasswordAuthentication:
3256 intptr = &options->password_authentication;
3257 goto parse_flag;
3258@@ -2777,6 +2820,10 @@ dump_config(ServerOptions *o)
3259 #ifdef GSSAPI
3260 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
3261 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
3262+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
3263+ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
3264+ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
3265+ dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
3266 #endif
3267 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
3268 dump_cfg_fmtint(sKbdInteractiveAuthentication,
3269diff --git a/servconf.h b/servconf.h
3270index 4202a2d02..3f47ea25e 100644
3271--- a/servconf.h
3272+++ b/servconf.h
3273@@ -132,8 +132,11 @@ typedef struct {
3274 int kerberos_get_afs_token; /* If true, try to get AFS token if
3275 * authenticated with Kerberos. */
3276 int gss_authentication; /* If true, permit GSSAPI authentication */
3277+ int gss_keyex; /* If true, permit GSSAPI key exchange */
3278 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
3279 int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
3280+ int gss_store_rekey;
3281+ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */
3282 int password_authentication; /* If true, permit password
3283 * authentication. */
3284 int kbd_interactive_authentication; /* If true, permit */
3285diff --git a/session.c b/session.c
3286index 8c0e54f79..06a33442a 100644
3287--- a/session.c
3288+++ b/session.c
3289@@ -2678,13 +2678,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
3290
3291 #ifdef KRB5
3292 if (options.kerberos_ticket_cleanup &&
3293- authctxt->krb5_ctx)
3294+ authctxt->krb5_ctx) {
3295+ temporarily_use_uid(authctxt->pw);
3296 krb5_cleanup_proc(authctxt);
3297+ restore_uid();
3298+ }
3299 #endif
3300
3301 #ifdef GSSAPI
3302- if (options.gss_cleanup_creds)
3303+ if (options.gss_cleanup_creds) {
3304+ temporarily_use_uid(authctxt->pw);
3305 ssh_gssapi_cleanup_creds();
3306+ restore_uid();
3307+ }
3308 #endif
3309
3310 /* remove agent socket */
3311diff --git a/ssh-gss.h b/ssh-gss.h
3312index 36180d07a..70dd36658 100644
3313--- a/ssh-gss.h
3314+++ b/ssh-gss.h
3315@@ -1,6 +1,6 @@
3316 /* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */
3317 /*
3318- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
3319+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3320 *
3321 * Redistribution and use in source and binary forms, with or without
3322 * modification, are permitted provided that the following conditions
3323@@ -61,10 +61,30 @@
3324
3325 #define SSH_GSS_OIDTYPE 0x06
3326
3327+#define SSH2_MSG_KEXGSS_INIT 30
3328+#define SSH2_MSG_KEXGSS_CONTINUE 31
3329+#define SSH2_MSG_KEXGSS_COMPLETE 32
3330+#define SSH2_MSG_KEXGSS_HOSTKEY 33
3331+#define SSH2_MSG_KEXGSS_ERROR 34
3332+#define SSH2_MSG_KEXGSS_GROUPREQ 40
3333+#define SSH2_MSG_KEXGSS_GROUP 41
3334+#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
3335+#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
3336+#define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-"
3337+#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-"
3338+#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
3339+#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-"
3340+#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-"
3341+
3342+#define GSS_KEX_DEFAULT_KEX \
3343+ KEX_GSS_GEX_SHA1_ID "," \
3344+ KEX_GSS_GRP14_SHA1_ID
3345+
3346 typedef struct {
3347 char *filename;
3348 char *envvar;
3349 char *envval;
3350+ struct passwd *owner;
3351 void *data;
3352 } ssh_gssapi_ccache;
3353
3354@@ -72,8 +92,11 @@ typedef struct {
3355 gss_buffer_desc displayname;
3356 gss_buffer_desc exportedname;
3357 gss_cred_id_t creds;
3358+ gss_name_t name;
3359 struct ssh_gssapi_mech_struct *mech;
3360 ssh_gssapi_ccache store;
3361+ int used;
3362+ int updated;
3363 } ssh_gssapi_client;
3364
3365 typedef struct ssh_gssapi_mech_struct {
3366@@ -84,6 +107,7 @@ typedef struct ssh_gssapi_mech_struct {
3367 int (*userok) (ssh_gssapi_client *, char *);
3368 int (*localname) (ssh_gssapi_client *, char **);
3369 void (*storecreds) (ssh_gssapi_client *);
3370+ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
3371 } ssh_gssapi_mech;
3372
3373 typedef struct {
3374@@ -94,10 +118,11 @@ typedef struct {
3375 gss_OID oid; /* client */
3376 gss_cred_id_t creds; /* server */
3377 gss_name_t client; /* server */
3378- gss_cred_id_t client_creds; /* server */
3379+ gss_cred_id_t client_creds; /* both */
3380 } Gssctxt;
3381
3382 extern ssh_gssapi_mech *supported_mechs[];
3383+extern Gssctxt *gss_kex_context;
3384
3385 int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
3386 void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
3387@@ -109,6 +134,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *);
3388
3389 struct sshbuf;
3390 int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *);
3391+int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *);
3392
3393 OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
3394 OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
3395@@ -123,17 +149,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **);
3396 OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
3397 void ssh_gssapi_buildmic(struct sshbuf *, const char *,
3398 const char *, const char *);
3399-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
3400+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
3401+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
3402+int ssh_gssapi_credentials_updated(Gssctxt *);
3403
3404 /* In the server */
3405+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
3406+ const char *);
3407+char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
3408+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
3409+ const char *, const char *);
3410+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
3411+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
3412+ const char *);
3413 OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
3414-int ssh_gssapi_userok(char *name);
3415+int ssh_gssapi_userok(char *name, struct passwd *, int kex);
3416 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
3417 void ssh_gssapi_do_child(char ***, u_int *);
3418 void ssh_gssapi_cleanup_creds(void);
3419 void ssh_gssapi_storecreds(void);
3420 const char *ssh_gssapi_displayname(void);
3421
3422+char *ssh_gssapi_server_mechanisms(void);
3423+int ssh_gssapi_oid_table_ok(void);
3424+
3425+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
3426+void ssh_gssapi_rekey_creds(void);
3427+
3428 #endif /* GSSAPI */
3429
3430 #endif /* _SSH_GSS_H */
3431diff --git a/ssh.1 b/ssh.1
3432index 60de6087a..db5c65bc7 100644
3433--- a/ssh.1
3434+++ b/ssh.1
3435@@ -503,7 +503,13 @@ For full details of the options listed below, and their possible values, see
3436 .It GatewayPorts
3437 .It GlobalKnownHostsFile
3438 .It GSSAPIAuthentication
3439+.It GSSAPIKeyExchange
3440+.It GSSAPIClientIdentity
3441 .It GSSAPIDelegateCredentials
3442+.It GSSAPIKexAlgorithms
3443+.It GSSAPIRenewalForcesRekey
3444+.It GSSAPIServerIdentity
3445+.It GSSAPITrustDns
3446 .It HashKnownHosts
3447 .It Host
3448 .It HostbasedAuthentication
3449@@ -579,6 +585,8 @@ flag),
3450 (supported message integrity codes),
3451 .Ar kex
3452 (key exchange algorithms),
3453+.Ar kex-gss
3454+(GSSAPI key exchange algorithms),
3455 .Ar key
3456 (key types),
3457 .Ar key-cert
3458diff --git a/ssh.c b/ssh.c
3459index 15aee569e..110cf9c19 100644
3460--- a/ssh.c
3461+++ b/ssh.c
3462@@ -747,6 +747,8 @@ main(int ac, char **av)
3463 else if (strcmp(optarg, "kex") == 0 ||
3464 strcasecmp(optarg, "KexAlgorithms") == 0)
3465 cp = kex_alg_list('\n');
3466+ else if (strcmp(optarg, "kex-gss") == 0)
3467+ cp = kex_gss_alg_list('\n');
3468 else if (strcmp(optarg, "key") == 0)
3469 cp = sshkey_alg_list(0, 0, 0, '\n');
3470 else if (strcmp(optarg, "key-cert") == 0)
3471@@ -772,8 +774,8 @@ main(int ac, char **av)
3472 } else if (strcmp(optarg, "help") == 0) {
3473 cp = xstrdup(
3474 "cipher\ncipher-auth\ncompression\nkex\n"
3475- "key\nkey-cert\nkey-plain\nkey-sig\nmac\n"
3476- "protocol-version\nsig");
3477+ "kex-gss\nkey\nkey-cert\nkey-plain\n"
3478+ "key-sig\nmac\nprotocol-version\nsig");
3479 }
3480 if (cp == NULL)
3481 fatal("Unsupported query \"%s\"", optarg);
3482diff --git a/ssh_config b/ssh_config
3483index 5e8ef548b..1ff999b68 100644
3484--- a/ssh_config
3485+++ b/ssh_config
3486@@ -24,6 +24,8 @@
3487 # HostbasedAuthentication no
3488 # GSSAPIAuthentication no
3489 # GSSAPIDelegateCredentials no
3490+# GSSAPIKeyExchange no
3491+# GSSAPITrustDNS no
3492 # BatchMode no
3493 # CheckHostIP yes
3494 # AddressFamily any
3495diff --git a/ssh_config.5 b/ssh_config.5
3496index 06a32d314..3f4906972 100644
3497--- a/ssh_config.5
3498+++ b/ssh_config.5
3499@@ -766,10 +766,67 @@ The default is
3500 Specifies whether user authentication based on GSSAPI is allowed.
3501 The default is
3502 .Cm no .
3503+.It Cm GSSAPIClientIdentity
3504+If set, specifies the GSSAPI client identity that ssh should use when
3505+connecting to the server. The default is unset, which means that the default
3506+identity will be used.
3507 .It Cm GSSAPIDelegateCredentials
3508 Forward (delegate) credentials to the server.
3509 The default is
3510 .Cm no .
3511+.It Cm GSSAPIKeyExchange
3512+Specifies whether key exchange based on GSSAPI may be used. When using
3513+GSSAPI key exchange the server need not have a host key.
3514+The default is
3515+.Dq no .
3516+.It Cm GSSAPIRenewalForcesRekey
3517+If set to
3518+.Dq yes
3519+then renewal of the client's GSSAPI credentials will force the rekeying of the
3520+ssh connection. With a compatible server, this will delegate the renewed
3521+credentials to a session on the server.
3522+.Pp
3523+Checks are made to ensure that credentials are only propagated when the new
3524+credentials match the old ones on the originating client and where the
3525+receiving server still has the old set in its cache.
3526+.Pp
3527+The default is
3528+.Dq no .
3529+.Pp
3530+For this to work
3531+.Cm GSSAPIKeyExchange
3532+needs to be enabled in the server and also used by the client.
3533+.It Cm GSSAPIServerIdentity
3534+If set, specifies the GSSAPI server identity that ssh should expect when
3535+connecting to the server. The default is unset, which means that the
3536+expected GSSAPI server identity will be determined from the target
3537+hostname.
3538+.It Cm GSSAPITrustDns
3539+Set to
3540+.Dq yes
3541+to indicate that the DNS is trusted to securely canonicalize
3542+the name of the host being connected to. If
3543+.Dq no ,
3544+the hostname entered on the
3545+command line will be passed untouched to the GSSAPI library.
3546+The default is
3547+.Dq no .
3548+.It Cm GSSAPIKexAlgorithms
3549+The list of key exchange algorithms that are offered for GSSAPI
3550+key exchange. Possible values are
3551+.Bd -literal -offset 3n
3552+gss-gex-sha1-,
3553+gss-group1-sha1-,
3554+gss-group14-sha1-,
3555+gss-group14-sha256-,
3556+gss-group16-sha512-,
3557+gss-nistp256-sha256-,
3558+gss-curve25519-sha256-
3559+.Ed
3560+.Pp
3561+The default is
3562+.Dq gss-gex-sha1-,gss-group14-sha1- .
3563+This option only applies to protocol version 2 connections using GSSAPI.
3564 .It Cm HashKnownHosts
3565 Indicates that
3566 .Xr ssh 1
3567diff --git a/sshconnect2.c b/sshconnect2.c
3568index af00fb30c..03bc87eb4 100644
3569--- a/sshconnect2.c
3570+++ b/sshconnect2.c
3571@@ -80,8 +80,6 @@
3572 #endif
3573
3574 /* import */
3575-extern char *client_version_string;
3576-extern char *server_version_string;
3577 extern Options options;
3578
3579 /*
3580@@ -163,6 +161,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
3581 char *s, *all_key;
3582 int r, use_known_hosts_order = 0;
3583
3584+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3585+ char *orig = NULL, *gss = NULL;
3586+ char *gss_host = NULL;
3587+#endif
3588+
3589 xxx_host = host;
3590 xxx_hostaddr = hostaddr;
3591
3592@@ -206,6 +209,35 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
3593 compat_pkalg_proposal(options.hostkeyalgorithms);
3594 }
3595
3596+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3597+ if (options.gss_keyex) {
3598+ /* Add the GSSAPI mechanisms currently supported on this
3599+ * client to the key exchange algorithm proposal */
3600+ orig = myproposal[PROPOSAL_KEX_ALGS];
3601+
3602+ if (options.gss_server_identity)
3603+ gss_host = xstrdup(options.gss_server_identity);
3604+ else if (options.gss_trust_dns)
3605+ gss_host = remote_hostname(ssh);
3606+ else
3607+ gss_host = xstrdup(host);
3608+
3609+ gss = ssh_gssapi_client_mechanisms(gss_host,
3610+ options.gss_client_identity, options.gss_kex_algorithms);
3611+ if (gss) {
3612+ debug("Offering GSSAPI proposal: %s", gss);
3613+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
3614+ "%s,%s", gss, orig);
3615+
3616+ /* If we've got GSSAPI algorithms, then we also support the
3617+ * 'null' hostkey, as a last resort */
3618+ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
3619+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
3620+ "%s,null", orig);
3621+ }
3622+ }
3623+#endif
3624+
3625 if (options.rekey_limit || options.rekey_interval)
3626 ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
3627 options.rekey_interval);
3628@@ -224,16 +256,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
3629 # ifdef OPENSSL_HAS_ECC
3630 ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
3631 # endif
3632-#endif
3633+# ifdef GSSAPI
3634+ if (options.gss_keyex) {
3635+ ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
3636+ ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
3637+ ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
3638+ ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
3639+ ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
3640+ ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
3641+ ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
3642+ }
3643+# endif
3644+#endif /* WITH_OPENSSL */
3645 ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
3646 ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client;
3647 ssh->kex->verify_host_key=&verify_host_key_callback;
3648
3649+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3650+ if (options.gss_keyex) {
3651+ ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
3652+ ssh->kex->gss_trust_dns = options.gss_trust_dns;
3653+ ssh->kex->gss_client = options.gss_client_identity;
3654+ ssh->kex->gss_host = gss_host;
3655+ }
3656+#endif
3657+
3658 ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
3659
3660 /* remove ext-info from the KEX proposals for rekeying */
3661 myproposal[PROPOSAL_KEX_ALGS] =
3662 compat_kex_proposal(options.kex_algorithms);
3663+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3664+ /* repair myproposal after it was crumpled by the */
3665+ /* ext-info removal above */
3666+ if (gss) {
3667+ orig = myproposal[PROPOSAL_KEX_ALGS];
3668+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
3669+ "%s,%s", gss, orig);
3670+ free(gss);
3671+ }
3672+#endif
3673 if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0)
3674 fatal("kex_prop2buf: %s", ssh_err(r));
3675
3676@@ -330,6 +392,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *);
3677 static int input_gssapi_token(int type, u_int32_t, struct ssh *);
3678 static int input_gssapi_error(int, u_int32_t, struct ssh *);
3679 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
3680+static int userauth_gsskeyex(struct ssh *);
3681 #endif
3682
3683 void userauth(struct ssh *, char *);
3684@@ -346,6 +409,11 @@ static char *authmethods_get(void);
3685
3686 Authmethod authmethods[] = {
3687 #ifdef GSSAPI
3688+ {"gssapi-keyex",
3689+ userauth_gsskeyex,
3690+ NULL,
3691+ &options.gss_keyex,
3692+ NULL},
3693 {"gssapi-with-mic",
3694 userauth_gssapi,
3695 userauth_gssapi_cleanup,
3696@@ -716,12 +784,25 @@ userauth_gssapi(struct ssh *ssh)
3697 OM_uint32 min;
3698 int r, ok = 0;
3699 gss_OID mech = NULL;
3700+ char *gss_host;
3701+
3702+ if (options.gss_server_identity)
3703+ gss_host = xstrdup(options.gss_server_identity);
3704+ else if (options.gss_trust_dns)
3705+ gss_host = remote_hostname(ssh);
3706+ else
3707+ gss_host = xstrdup(authctxt->host);
3708
3709 /* Try one GSSAPI method at a time, rather than sending them all at
3710 * once. */
3711
3712 if (authctxt->gss_supported_mechs == NULL)
3713- gss_indicate_mechs(&min, &authctxt->gss_supported_mechs);
3714+ if (GSS_ERROR(gss_indicate_mechs(&min,
3715+ &authctxt->gss_supported_mechs))) {
3716+ authctxt->gss_supported_mechs = NULL;
3717+ free(gss_host);
3718+ return 0;
3719+ }
3720
3721 /* Check to see whether the mechanism is usable before we offer it */
3722 while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
3723@@ -730,13 +811,15 @@ userauth_gssapi(struct ssh *ssh)
3724 elements[authctxt->mech_tried];
3725 /* My DER encoding requires length<128 */
3726 if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
3727- mech, authctxt->host)) {
3728+ mech, gss_host, options.gss_client_identity)) {
3729 ok = 1; /* Mechanism works */
3730 } else {
3731 authctxt->mech_tried++;
3732 }
3733 }
3734
3735+ free(gss_host);
3736+
3737 if (!ok || mech == NULL)
3738 return 0;
3739
3740@@ -976,6 +1059,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
3741 free(lang);
3742 return r;
3743 }
3744+
3745+int
3746+userauth_gsskeyex(struct ssh *ssh)
3747+{
3748+ struct sshbuf *b = NULL;
3749+ Authctxt *authctxt = ssh->authctxt;
3750+ gss_buffer_desc gssbuf;
3751+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
3752+ OM_uint32 ms;
3753+ int r;
3754+
3755+ static int attempt = 0;
3756+ if (attempt++ >= 1)
3757+ return (0);
3758+
3759+ if (gss_kex_context == NULL) {
3760+ debug("No valid Key exchange context");
3761+ return (0);
3762+ }
3763+
3764+ if ((b = sshbuf_new()) == NULL)
3765+ fatal("%s: sshbuf_new failed", __func__);
3766+
3767+ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
3768+ "gssapi-keyex");
3769+
3770+ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
3771+ fatal("%s: sshbuf_mutable_ptr failed", __func__);
3772+ gssbuf.length = sshbuf_len(b);
3773+
3774+ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
3775+ sshbuf_free(b);
3776+ return (0);
3777+ }
3778+
3779+ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
3780+ (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
3781+ (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
3782+ (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
3783+ (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
3784+ (r = sshpkt_send(ssh)) != 0)
3785+ fatal("%s: %s", __func__, ssh_err(r));
3786+
3787+ sshbuf_free(b);
3788+ gss_release_buffer(&ms, &mic);
3789+
3790+ return (1);
3791+}
3792+
3793 #endif /* GSSAPI */
3794
3795 static int
3796diff --git a/sshd.c b/sshd.c
3797index 60b2aaf73..d92f03aaf 100644
3798--- a/sshd.c
3799+++ b/sshd.c
3800@@ -817,8 +817,8 @@ notify_hostkeys(struct ssh *ssh)
3801 }
3802 debug3("%s: sent %u hostkeys", __func__, nkeys);
3803 if (nkeys == 0)
3804- fatal("%s: no hostkeys", __func__);
3805- if ((r = sshpkt_send(ssh)) != 0)
3806+ debug3("%s: no hostkeys", __func__);
3807+ else if ((r = sshpkt_send(ssh)) != 0)
3808 sshpkt_fatal(ssh, r, "%s: send", __func__);
3809 sshbuf_free(buf);
3810 }
3811@@ -1852,7 +1852,8 @@ main(int ac, char **av)
3812 free(fp);
3813 }
3814 accumulate_host_timing_secret(cfg, NULL);
3815- if (!sensitive_data.have_ssh2_key) {
3816+ /* The GSSAPI key exchange can run without a host key */
3817+ if (!sensitive_data.have_ssh2_key && !options.gss_keyex) {
3818 logit("sshd: no hostkeys available -- exiting.");
3819 exit(1);
3820 }
3821@@ -2347,6 +2348,48 @@ do_ssh2_kex(struct ssh *ssh)
3822 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
3823 list_hostkey_types());
3824
3825+#if defined(GSSAPI) && defined(WITH_OPENSSL)
3826+ {
3827+ char *orig;
3828+ char *gss = NULL;
3829+ char *newstr = NULL;
3830+ orig = myproposal[PROPOSAL_KEX_ALGS];
3831+
3832+ /*
3833+ * If we don't have a host key, then there's no point advertising
3834+ * the other key exchange algorithms
3835+ */
3836+
3837+ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
3838+ orig = NULL;
3839+
3840+ if (options.gss_keyex)
3841+ gss = ssh_gssapi_server_mechanisms();
3842+ else
3843+ gss = NULL;
3844+
3845+ if (gss && orig)
3846+ xasprintf(&newstr, "%s,%s", gss, orig);
3847+ else if (gss)
3848+ newstr = gss;
3849+ else if (orig)
3850+ newstr = orig;
3851+
3852+ /*
3853+ * If we've got GSSAPI mechanisms, then we've got the 'null' host
3854+ * key alg, but we can't tell people about it unless its the only
3855+ * host key algorithm we support
3856+ */
3857+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
3858+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
3859+
3860+ if (newstr)
3861+ myproposal[PROPOSAL_KEX_ALGS] = newstr;
3862+ else
3863+ fatal("No supported key exchange algorithms");
3864+ }
3865+#endif
3866+
3867 /* start key exchange */
3868 if ((r = kex_setup(ssh, myproposal)) != 0)
3869 fatal("kex_setup: %s", ssh_err(r));
3870@@ -2362,7 +2405,18 @@ do_ssh2_kex(struct ssh *ssh)
3871 # ifdef OPENSSL_HAS_ECC
3872 kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
3873 # endif
3874-#endif
3875+# ifdef GSSAPI
3876+ if (options.gss_keyex) {
3877+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
3878+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
3879+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server;
3880+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
3881+ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
3882+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
3883+ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
3884+ }
3885+# endif
3886+#endif /* WITH_OPENSSL */
3887 kex->kex[KEX_C25519_SHA256] = kex_gen_server;
3888 kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server;
3889 kex->load_host_public_key=&get_hostkey_public_by_type;
3890diff --git a/sshd_config b/sshd_config
3891index 19b7c91a1..2c48105f8 100644
3892--- a/sshd_config
3893+++ b/sshd_config
3894@@ -69,6 +69,8 @@ AuthorizedKeysFile .ssh/authorized_keys
3895 # GSSAPI options
3896 #GSSAPIAuthentication no
3897 #GSSAPICleanupCredentials yes
3898+#GSSAPIStrictAcceptorCheck yes
3899+#GSSAPIKeyExchange no
3900
3901 # Set this to 'yes' to enable PAM authentication, account processing,
3902 # and session processing. If this is enabled, PAM authentication will
3903diff --git a/sshd_config.5 b/sshd_config.5
3904index 70ccea449..f6b41a2f8 100644
3905--- a/sshd_config.5
3906+++ b/sshd_config.5
3907@@ -646,6 +646,11 @@ Specifies whether to automatically destroy the user's credentials cache
3908 on logout.
3909 The default is
3910 .Cm yes .
3911+.It Cm GSSAPIKeyExchange
3912+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
3913+doesn't rely on ssh keys to verify host identity.
3914+The default is
3915+.Cm no .
3916 .It Cm GSSAPIStrictAcceptorCheck
3917 Determines whether to be strict about the identity of the GSSAPI acceptor
3918 a client authenticates against.
3919@@ -660,6 +665,31 @@ machine's default store.
3920 This facility is provided to assist with operation on multi homed machines.
3921 The default is
3922 .Cm yes .
3923+.It Cm GSSAPIStoreCredentialsOnRekey
3924+Controls whether the user's GSSAPI credentials should be updated following a
3925+successful connection rekeying. This option can be used to accepted renewed
3926+or updated credentials from a compatible client. The default is
3927+.Dq no .
3928+.Pp
3929+For this to work
3930+.Cm GSSAPIKeyExchange
3931+needs to be enabled in the server and also used by the client.
3932+.It Cm GSSAPIKexAlgorithms
3933+The list of key exchange algorithms that are accepted by GSSAPI
3934+key exchange. Possible values are
3935+.Bd -literal -offset 3n
3936+gss-gex-sha1-,
3937+gss-group1-sha1-,
3938+gss-group14-sha1-,
3939+gss-group14-sha256-,
3940+gss-group16-sha512-,
3941+gss-nistp256-sha256-,
3942+gss-curve25519-sha256-
3943+.Ed
3944+.Pp
3945+The default is
3946+.Dq gss-gex-sha1-,gss-group14-sha1- .
3947+This option only applies to protocol version 2 connections using GSSAPI.
3948 .It Cm HostbasedAcceptedKeyTypes
3949 Specifies the key types that will be accepted for hostbased authentication
3950 as a list of comma-separated patterns.
3951diff --git a/sshkey.c b/sshkey.c
3952index 57995ee68..fd5b77246 100644
3953--- a/sshkey.c
3954+++ b/sshkey.c
3955@@ -154,6 +154,7 @@ static const struct keytype keytypes[] = {
3956 KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 },
3957 # endif /* OPENSSL_HAS_ECC */
3958 #endif /* WITH_OPENSSL */
3959+ { "null", "null", NULL, KEY_NULL, 0, 0, 0 },
3960 { NULL, NULL, NULL, -1, -1, 0, 0 }
3961 };
3962
3963@@ -255,7 +256,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
3964 const struct keytype *kt;
3965
3966 for (kt = keytypes; kt->type != -1; kt++) {
3967- if (kt->name == NULL)
3968+ if (kt->name == NULL || kt->type == KEY_NULL)
3969 continue;
3970 if (!include_sigonly && kt->sigonly)
3971 continue;
3972diff --git a/sshkey.h b/sshkey.h
3973index 71a3fddcb..37a43a67a 100644
3974--- a/sshkey.h
3975+++ b/sshkey.h
3976@@ -69,6 +69,7 @@ enum sshkey_types {
3977 KEY_ECDSA_SK_CERT,
3978 KEY_ED25519_SK,
3979 KEY_ED25519_SK_CERT,
3980+ KEY_NULL,
3981 KEY_UNSPEC
3982 };
3983
diff --git a/debian/patches/keepalive-extensions.patch b/debian/patches/keepalive-extensions.patch
new file mode 100644
index 000000000..734118a19
--- /dev/null
+++ b/debian/patches/keepalive-extensions.patch
@@ -0,0 +1,135 @@
1From 3558be2914c0127489faae40ce2eae66142c3287 Mon Sep 17 00:00:00 2001
2From: Richard Kettlewell <rjk@greenend.org.uk>
3Date: Sun, 9 Feb 2014 16:09:52 +0000
4Subject: Various keepalive extensions
5
6Add compatibility aliases for ProtocolKeepAlives and SetupTimeOut, supported
7in previous versions of Debian's OpenSSH package but since superseded by
8ServerAliveInterval. (We're probably stuck with this bit for
9compatibility.)
10
11In batch mode, default ServerAliveInterval to five minutes.
12
13Adjust documentation to match and to give some more advice on use of
14keepalives.
15
16Author: Ian Jackson <ian@chiark.greenend.org.uk>
17Author: Matthew Vernon <matthew@debian.org>
18Author: Colin Watson <cjwatson@debian.org>
19Last-Update: 2020-02-21
20
21Patch-Name: keepalive-extensions.patch
22---
23 readconf.c | 14 ++++++++++++--
24 ssh_config.5 | 21 +++++++++++++++++++--
25 sshd_config.5 | 3 +++
26 3 files changed, 34 insertions(+), 4 deletions(-)
27
28diff --git a/readconf.c b/readconf.c
29index 0fc996871..2399208f8 100644
30--- a/readconf.c
31+++ b/readconf.c
32@@ -176,6 +176,7 @@ typedef enum {
33 oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
34 oPubkeyAcceptedKeyTypes, oCASignatureAlgorithms, oProxyJump,
35 oSecurityKeyProvider,
36+ oProtocolKeepAlives, oSetupTimeOut,
37 oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
38 } OpCodes;
39
40@@ -326,6 +327,8 @@ static struct {
41 { "ignoreunknown", oIgnoreUnknown },
42 { "proxyjump", oProxyJump },
43 { "securitykeyprovider", oSecurityKeyProvider },
44+ { "protocolkeepalives", oProtocolKeepAlives },
45+ { "setuptimeout", oSetupTimeOut },
46
47 { NULL, oBadOption }
48 };
49@@ -1495,6 +1498,8 @@ parse_keytypes:
50 goto parse_flag;
51
52 case oServerAliveInterval:
53+ case oProtocolKeepAlives: /* Debian-specific compatibility alias */
54+ case oSetupTimeOut: /* Debian-specific compatibility alias */
55 intptr = &options->server_alive_interval;
56 goto parse_time;
57
58@@ -2198,8 +2203,13 @@ fill_default_options(Options * options)
59 options->rekey_interval = 0;
60 if (options->verify_host_key_dns == -1)
61 options->verify_host_key_dns = 0;
62- if (options->server_alive_interval == -1)
63- options->server_alive_interval = 0;
64+ if (options->server_alive_interval == -1) {
65+ /* in batch mode, default is 5mins */
66+ if (options->batch_mode == 1)
67+ options->server_alive_interval = 300;
68+ else
69+ options->server_alive_interval = 0;
70+ }
71 if (options->server_alive_count_max == -1)
72 options->server_alive_count_max = 3;
73 if (options->control_master == -1)
74diff --git a/ssh_config.5 b/ssh_config.5
75index 3f4906972..3079db19b 100644
76--- a/ssh_config.5
77+++ b/ssh_config.5
78@@ -266,9 +266,13 @@ If set to
79 .Cm yes ,
80 user interaction such as password prompts and host key confirmation requests
81 will be disabled.
82+In addition, the
83+.Cm ServerAliveInterval
84+option will be set to 300 seconds by default (Debian-specific).
85 This option is useful in scripts and other batch jobs where no user
86 is present to interact with
87-.Xr ssh 1 .
88+.Xr ssh 1 ,
89+and where it is desirable to detect a broken network swiftly.
90 The argument must be
91 .Cm yes
92 or
93@@ -1593,7 +1597,14 @@ from the server,
94 will send a message through the encrypted
95 channel to request a response from the server.
96 The default
97-is 0, indicating that these messages will not be sent to the server.
98+is 0, indicating that these messages will not be sent to the server,
99+or 300 if the
100+.Cm BatchMode
101+option is set (Debian-specific).
102+.Cm ProtocolKeepAlives
103+and
104+.Cm SetupTimeOut
105+are Debian-specific compatibility aliases for this option.
106 .It Cm SetEnv
107 Directly specify one or more environment variables and their contents to
108 be sent to the server.
109@@ -1673,6 +1684,12 @@ Specifies whether the system should send TCP keepalive messages to the
110 other side.
111 If they are sent, death of the connection or crash of one
112 of the machines will be properly noticed.
113+This option only uses TCP keepalives (as opposed to using ssh level
114+keepalives), so takes a long time to notice when the connection dies.
115+As such, you probably want
116+the
117+.Cm ServerAliveInterval
118+option as well.
119 However, this means that
120 connections will die if the route is down temporarily, and some people
121 find it annoying.
122diff --git a/sshd_config.5 b/sshd_config.5
123index f6b41a2f8..ebd09f891 100644
124--- a/sshd_config.5
125+++ b/sshd_config.5
126@@ -1668,6 +1668,9 @@ This avoids infinitely hanging sessions.
127 .Pp
128 To disable TCP keepalive messages, the value should be set to
129 .Cm no .
130+.Pp
131+This option was formerly called
132+.Cm KeepAlive .
133 .It Cm TrustedUserCAKeys
134 Specifies a file containing public keys of certificate authorities that are
135 trusted to sign user certificates for authentication, or
diff --git a/debian/patches/mention-ssh-keygen-on-keychange.patch b/debian/patches/mention-ssh-keygen-on-keychange.patch
new file mode 100644
index 000000000..6d48d7589
--- /dev/null
+++ b/debian/patches/mention-ssh-keygen-on-keychange.patch
@@ -0,0 +1,44 @@
1From c18e3c8125fc4553951705a1da8c86395d219bb1 Mon Sep 17 00:00:00 2001
2From: Scott Moser <smoser@ubuntu.com>
3Date: Sun, 9 Feb 2014 16:10:03 +0000
4Subject: Mention ssh-keygen in ssh fingerprint changed warning
5
6Author: Chris Lamb <lamby@debian.org>
7Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1843
8Bug-Ubuntu: https://bugs.launchpad.net/bugs/686607
9Last-Update: 2017-08-22
10
11Patch-Name: mention-ssh-keygen-on-keychange.patch
12---
13 sshconnect.c | 9 ++++++++-
14 1 file changed, 8 insertions(+), 1 deletion(-)
15
16diff --git a/sshconnect.c b/sshconnect.c
17index 4a5d4a003..b796d3c8a 100644
18--- a/sshconnect.c
19+++ b/sshconnect.c
20@@ -991,9 +991,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
21 error("%s. This could either mean that", key_msg);
22 error("DNS SPOOFING is happening or the IP address for the host");
23 error("and its host key have changed at the same time.");
24- if (ip_status != HOST_NEW)
25+ if (ip_status != HOST_NEW) {
26 error("Offending key for IP in %s:%lu",
27 ip_found->file, ip_found->line);
28+ error(" remove with:");
29+ error(" ssh-keygen -f \"%s\" -R \"%s\"",
30+ ip_found->file, ip);
31+ }
32 }
33 /* The host key has changed. */
34 warn_changed_key(host_key);
35@@ -1002,6 +1006,9 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
36 error("Offending %s key in %s:%lu",
37 sshkey_type(host_found->key),
38 host_found->file, host_found->line);
39+ error(" remove with:");
40+ error(" ssh-keygen -f \"%s\" -R \"%s\"",
41+ host_found->file, host);
42
43 /*
44 * If strict host key checking is in use, the user will have
diff --git a/debian/patches/no-openssl-version-status.patch b/debian/patches/no-openssl-version-status.patch
new file mode 100644
index 000000000..02a798b85
--- /dev/null
+++ b/debian/patches/no-openssl-version-status.patch
@@ -0,0 +1,62 @@
1From ba0377ab3e6b68f7ab747f500991a0445c7f4086 Mon Sep 17 00:00:00 2001
2From: Kurt Roeckx <kurt@roeckx.be>
3Date: Sun, 9 Feb 2014 16:10:14 +0000
4Subject: Don't check the status field of the OpenSSL version
5
6There is no reason to check the version of OpenSSL (in Debian). If it's
7not compatible the soname will change. OpenSSH seems to want to do a
8check for the soname based on the version number, but wants to keep the
9status of the release the same. Remove that check on the status since
10it doesn't tell you anything about how compatible that version is.
11
12Author: Colin Watson <cjwatson@debian.org>
13Bug-Debian: https://bugs.debian.org/93581
14Bug-Debian: https://bugs.debian.org/664383
15Bug-Debian: https://bugs.debian.org/732940
16Forwarded: not-needed
17Last-Update: 2014-10-07
18
19Patch-Name: no-openssl-version-status.patch
20---
21 openbsd-compat/openssl-compat.c | 6 +++---
22 openbsd-compat/regress/opensslvertest.c | 1 +
23 2 files changed, 4 insertions(+), 3 deletions(-)
24
25diff --git a/openbsd-compat/openssl-compat.c b/openbsd-compat/openssl-compat.c
26index a37ca61bf..c1749210d 100644
27--- a/openbsd-compat/openssl-compat.c
28+++ b/openbsd-compat/openssl-compat.c
29@@ -34,7 +34,7 @@
30 /*
31 * OpenSSL version numbers: MNNFFPPS: major minor fix patch status
32 * We match major, minor, fix and status (not patch) for <1.0.0.
33- * After that, we acceptable compatible fix versions (so we
34+ * After that, we accept compatible fix and status versions (so we
35 * allow 1.0.1 to work with 1.0.0). Going backwards is only allowed
36 * within a patch series.
37 */
38@@ -55,10 +55,10 @@ ssh_compatible_openssl(long headerver, long libver)
39 }
40
41 /*
42- * For versions >= 1.0.0, major,minor,status must match and library
43+ * For versions >= 1.0.0, major,minor must match and library
44 * fix version must be equal to or newer than the header.
45 */
46- mask = 0xfff0000fL; /* major,minor,status */
47+ mask = 0xfff00000L; /* major,minor */
48 hfix = (headerver & 0x000ff000) >> 12;
49 lfix = (libver & 0x000ff000) >> 12;
50 if ( (headerver & mask) == (libver & mask) && lfix >= hfix)
51diff --git a/openbsd-compat/regress/opensslvertest.c b/openbsd-compat/regress/opensslvertest.c
52index 5d019b598..58474873d 100644
53--- a/openbsd-compat/regress/opensslvertest.c
54+++ b/openbsd-compat/regress/opensslvertest.c
55@@ -35,6 +35,7 @@ struct version_test {
56
57 /* built with 1.0.1b release headers */
58 { 0x1000101fL, 0x1000101fL, 1},/* exact match */
59+ { 0x1000101fL, 0x10001010L, 1}, /* different status: ok */
60 { 0x1000101fL, 0x1000102fL, 1}, /* newer library patch version: ok */
61 { 0x1000101fL, 0x1000100fL, 1}, /* older library patch version: ok */
62 { 0x1000101fL, 0x1000201fL, 1}, /* newer library fix version: ok */
diff --git a/debian/patches/openbsd-docs.patch b/debian/patches/openbsd-docs.patch
new file mode 100644
index 000000000..34ec87094
--- /dev/null
+++ b/debian/patches/openbsd-docs.patch
@@ -0,0 +1,148 @@
1From 39fe318a4b572deeb3f7d03e55d319c0ab112a28 Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 9 Feb 2014 16:10:09 +0000
4Subject: Adjust various OpenBSD-specific references in manual pages
5
6No single bug reference for this patch, but history includes:
7 http://bugs.debian.org/154434 (login.conf(5))
8 http://bugs.debian.org/513417 (/etc/rc)
9 http://bugs.debian.org/530692 (ssl(8))
10 https://bugs.launchpad.net/bugs/456660 (ssl(8))
11
12Forwarded: not-needed
13Last-Update: 2017-10-04
14
15Patch-Name: openbsd-docs.patch
16---
17 moduli.5 | 4 ++--
18 ssh-keygen.1 | 12 ++++--------
19 ssh.1 | 4 ++++
20 sshd.8 | 5 ++---
21 sshd_config.5 | 3 +--
22 5 files changed, 13 insertions(+), 15 deletions(-)
23
24diff --git a/moduli.5 b/moduli.5
25index ef0de0850..149846c8c 100644
26--- a/moduli.5
27+++ b/moduli.5
28@@ -21,7 +21,7 @@
29 .Nd Diffie-Hellman moduli
30 .Sh DESCRIPTION
31 The
32-.Pa /etc/moduli
33+.Pa /etc/ssh/moduli
34 file contains prime numbers and generators for use by
35 .Xr sshd 8
36 in the Diffie-Hellman Group Exchange key exchange method.
37@@ -110,7 +110,7 @@ first estimates the size of the modulus required to produce enough
38 Diffie-Hellman output to sufficiently key the selected symmetric cipher.
39 .Xr sshd 8
40 then randomly selects a modulus from
41-.Fa /etc/moduli
42+.Fa /etc/ssh/moduli
43 that best meets the size requirement.
44 .Sh SEE ALSO
45 .Xr ssh-keygen 1 ,
46diff --git a/ssh-keygen.1 b/ssh-keygen.1
47index 7af564297..d6a7870e0 100644
48--- a/ssh-keygen.1
49+++ b/ssh-keygen.1
50@@ -196,9 +196,7 @@ key in
51 .Pa ~/.ssh/id_ed25519_sk
52 or
53 .Pa ~/.ssh/id_rsa .
54-Additionally, the system administrator may use this to generate host keys,
55-as seen in
56-.Pa /etc/rc .
57+Additionally, the system administrator may use this to generate host keys.
58 .Pp
59 Normally this program generates the key and asks for a file in which
60 to store the private key.
61@@ -261,9 +259,7 @@ If
62 .Fl f
63 has also been specified, its argument is used as a prefix to the
64 default path for the resulting host key files.
65-This is used by
66-.Pa /etc/rc
67-to generate new host keys.
68+This is used by system administration scripts to generate new host keys.
69 .It Fl a Ar rounds
70 When saving a private key, this option specifies the number of KDF
71 (key derivation function) rounds used.
72@@ -783,7 +779,7 @@ option.
73 Valid generator values are 2, 3, and 5.
74 .Pp
75 Screened DH groups may be installed in
76-.Pa /etc/moduli .
77+.Pa /etc/ssh/moduli .
78 It is important that this file contains moduli of a range of bit lengths and
79 that both ends of a connection share common moduli.
80 .Pp
81@@ -1154,7 +1150,7 @@ on all machines
82 where the user wishes to log in using public key authentication.
83 There is no need to keep the contents of this file secret.
84 .Pp
85-.It Pa /etc/moduli
86+.It Pa /etc/ssh/moduli
87 Contains Diffie-Hellman groups used for DH-GEX.
88 The file format is described in
89 .Xr moduli 5 .
90diff --git a/ssh.1 b/ssh.1
91index cf991e4ee..17b0e984f 100644
92--- a/ssh.1
93+++ b/ssh.1
94@@ -887,6 +887,10 @@ implements public key authentication protocol automatically,
95 using one of the DSA, ECDSA, Ed25519 or RSA algorithms.
96 The HISTORY section of
97 .Xr ssl 8
98+(on non-OpenBSD systems, see
99+.nh
100+http://www.openbsd.org/cgi\-bin/man.cgi?query=ssl&sektion=8#HISTORY)
101+.hy
102 contains a brief discussion of the DSA and RSA algorithms.
103 .Pp
104 The file
105diff --git a/sshd.8 b/sshd.8
106index 730520231..5ce0ea4fa 100644
107--- a/sshd.8
108+++ b/sshd.8
109@@ -65,7 +65,7 @@ over an insecure network.
110 .Nm
111 listens for connections from clients.
112 It is normally started at boot from
113-.Pa /etc/rc .
114+.Pa /etc/init.d/ssh .
115 It forks a new
116 daemon for each incoming connection.
117 The forked daemons handle
118@@ -904,7 +904,7 @@ This file is for host-based authentication (see
119 .Xr ssh 1 ) .
120 It should only be writable by root.
121 .Pp
122-.It Pa /etc/moduli
123+.It Pa /etc/ssh/moduli
124 Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange"
125 key exchange method.
126 The file format is described in
127@@ -1002,7 +1002,6 @@ The content of this file is not sensitive; it can be world-readable.
128 .Xr ssh-keyscan 1 ,
129 .Xr chroot 2 ,
130 .Xr hosts_access 5 ,
131-.Xr login.conf 5 ,
132 .Xr moduli 5 ,
133 .Xr sshd_config 5 ,
134 .Xr inetd 8 ,
135diff --git a/sshd_config.5 b/sshd_config.5
136index c926f584c..25f4b8117 100644
137--- a/sshd_config.5
138+++ b/sshd_config.5
139@@ -387,8 +387,7 @@ Certificates signed using other algorithms will not be accepted for
140 public key or host-based authentication.
141 .It Cm ChallengeResponseAuthentication
142 Specifies whether challenge-response authentication is allowed (e.g. via
143-PAM or through authentication styles supported in
144-.Xr login.conf 5 )
145+PAM).
146 The default is
147 .Cm yes .
148 .It Cm ChrootDirectory
diff --git a/debian/patches/package-versioning.patch b/debian/patches/package-versioning.patch
new file mode 100644
index 000000000..32a7a1fed
--- /dev/null
+++ b/debian/patches/package-versioning.patch
@@ -0,0 +1,47 @@
1From a4f868858c3395cacb59c58786b501317b9a3d03 Mon Sep 17 00:00:00 2001
2From: Matthew Vernon <matthew@debian.org>
3Date: Sun, 9 Feb 2014 16:10:05 +0000
4Subject: Include the Debian version in our identification
5
6This makes it easier to audit networks for versions patched against security
7vulnerabilities. It has little detrimental effect, as attackers will
8generally just try attacks rather than bothering to scan for
9vulnerable-looking version strings. (However, see debian-banner.patch.)
10
11Forwarded: not-needed
12Last-Update: 2019-06-05
13
14Patch-Name: package-versioning.patch
15---
16 kex.c | 2 +-
17 version.h | 7 ++++++-
18 2 files changed, 7 insertions(+), 2 deletions(-)
19
20diff --git a/kex.c b/kex.c
21index 574c76093..f638942d3 100644
22--- a/kex.c
23+++ b/kex.c
24@@ -1244,7 +1244,7 @@ kex_exchange_identification(struct ssh *ssh, int timeout_ms,
25 if (version_addendum != NULL && *version_addendum == '\0')
26 version_addendum = NULL;
27 if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n",
28- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
29+ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_RELEASE,
30 version_addendum == NULL ? "" : " ",
31 version_addendum == NULL ? "" : version_addendum)) != 0) {
32 error("%s: sshbuf_putf: %s", __func__, ssh_err(r));
33diff --git a/version.h b/version.h
34index c2affcb2a..d79126cc3 100644
35--- a/version.h
36+++ b/version.h
37@@ -3,4 +3,9 @@
38 #define SSH_VERSION "OpenSSH_8.2"
39
40 #define SSH_PORTABLE "p1"
41-#define SSH_RELEASE SSH_VERSION SSH_PORTABLE
42+#define SSH_RELEASE_MINIMUM SSH_VERSION SSH_PORTABLE
43+#ifdef SSH_EXTRAVERSION
44+#define SSH_RELEASE SSH_RELEASE_MINIMUM " " SSH_EXTRAVERSION
45+#else
46+#define SSH_RELEASE SSH_RELEASE_MINIMUM
47+#endif
diff --git a/debian/patches/restore-authorized_keys2.patch b/debian/patches/restore-authorized_keys2.patch
new file mode 100644
index 000000000..7281395ae
--- /dev/null
+++ b/debian/patches/restore-authorized_keys2.patch
@@ -0,0 +1,35 @@
1From 2fe72c4e855be0fc87dbdc296632394b6cfe957a Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 5 Mar 2017 02:02:11 +0000
4Subject: Restore reading authorized_keys2 by default
5
6Upstream seems to intend to gradually phase this out, so don't assume
7that this will remain the default forever. However, we were late in
8adopting the upstream sshd_config changes, so it makes sense to extend
9the grace period.
10
11Bug-Debian: https://bugs.debian.org/852320
12Forwarded: not-needed
13Last-Update: 2017-03-05
14
15Patch-Name: restore-authorized_keys2.patch
16---
17 sshd_config | 5 ++---
18 1 file changed, 2 insertions(+), 3 deletions(-)
19
20diff --git a/sshd_config b/sshd_config
21index ed8272f6d..ee9629102 100644
22--- a/sshd_config
23+++ b/sshd_config
24@@ -36,9 +36,8 @@
25
26 #PubkeyAuthentication yes
27
28-# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
29-# but this is overridden so installations will only check .ssh/authorized_keys
30-AuthorizedKeysFile .ssh/authorized_keys
31+# Expect .ssh/authorized_keys2 to be disregarded by default in future.
32+#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
33
34 #AuthorizedPrincipalsFile none
35
diff --git a/debian/patches/restore-tcp-wrappers.patch b/debian/patches/restore-tcp-wrappers.patch
new file mode 100644
index 000000000..d73cc283c
--- /dev/null
+++ b/debian/patches/restore-tcp-wrappers.patch
@@ -0,0 +1,172 @@
1From 31d42cd8624f29508f772447e617ab043a6487d9 Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Tue, 7 Oct 2014 13:22:41 +0100
4Subject: Restore TCP wrappers support
5
6Support for TCP wrappers was dropped in OpenSSH 6.7. See this message
7and thread:
8
9 https://lists.mindrot.org/pipermail/openssh-unix-dev/2014-April/032497.html
10
11It is true that this reduces preauth attack surface in sshd. On the
12other hand, this support seems to be quite widely used, and abruptly
13dropping it (from the perspective of users who don't read
14openssh-unix-dev) could easily cause more serious problems in practice.
15
16It's not entirely clear what the right long-term answer for Debian is,
17but it at least probably doesn't involve dropping this feature shortly
18before a freeze.
19
20Forwarded: not-needed
21Last-Update: 2019-06-05
22
23Patch-Name: restore-tcp-wrappers.patch
24---
25 configure.ac | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++
26 sshd.8 | 7 +++++++
27 sshd.c | 25 +++++++++++++++++++++++
28 3 files changed, 89 insertions(+)
29
30diff --git a/configure.ac b/configure.ac
31index efafb6bd8..cee7cbc51 100644
32--- a/configure.ac
33+++ b/configure.ac
34@@ -1556,6 +1556,62 @@ else
35 AC_MSG_RESULT([no])
36 fi
37
38+# Check whether user wants TCP wrappers support
39+TCPW_MSG="no"
40+AC_ARG_WITH([tcp-wrappers],
41+ [ --with-tcp-wrappers[[=PATH]] Enable tcpwrappers support (optionally in PATH)],
42+ [
43+ if test "x$withval" != "xno" ; then
44+ saved_LIBS="$LIBS"
45+ saved_LDFLAGS="$LDFLAGS"
46+ saved_CPPFLAGS="$CPPFLAGS"
47+ if test -n "${withval}" && \
48+ test "x${withval}" != "xyes"; then
49+ if test -d "${withval}/lib"; then
50+ if test -n "${need_dash_r}"; then
51+ LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}"
52+ else
53+ LDFLAGS="-L${withval}/lib ${LDFLAGS}"
54+ fi
55+ else
56+ if test -n "${need_dash_r}"; then
57+ LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}"
58+ else
59+ LDFLAGS="-L${withval} ${LDFLAGS}"
60+ fi
61+ fi
62+ if test -d "${withval}/include"; then
63+ CPPFLAGS="-I${withval}/include ${CPPFLAGS}"
64+ else
65+ CPPFLAGS="-I${withval} ${CPPFLAGS}"
66+ fi
67+ fi
68+ LIBS="-lwrap $LIBS"
69+ AC_MSG_CHECKING([for libwrap])
70+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
71+#include <sys/types.h>
72+#include <sys/socket.h>
73+#include <netinet/in.h>
74+#include <tcpd.h>
75+int deny_severity = 0, allow_severity = 0;
76+ ]], [[
77+ hosts_access(0);
78+ ]])], [
79+ AC_MSG_RESULT([yes])
80+ AC_DEFINE([LIBWRAP], [1],
81+ [Define if you want
82+ TCP Wrappers support])
83+ SSHDLIBS="$SSHDLIBS -lwrap"
84+ TCPW_MSG="yes"
85+ ], [
86+ AC_MSG_ERROR([*** libwrap missing])
87+
88+ ])
89+ LIBS="$saved_LIBS"
90+ fi
91+ ]
92+)
93+
94 # Check whether user wants to use ldns
95 LDNS_MSG="no"
96 AC_ARG_WITH(ldns,
97@@ -5413,6 +5469,7 @@ echo " PAM support: $PAM_MSG"
98 echo " OSF SIA support: $SIA_MSG"
99 echo " KerberosV support: $KRB5_MSG"
100 echo " SELinux support: $SELINUX_MSG"
101+echo " TCP Wrappers support: $TCPW_MSG"
102 echo " MD5 password support: $MD5_MSG"
103 echo " libedit support: $LIBEDIT_MSG"
104 echo " libldns support: $LDNS_MSG"
105diff --git a/sshd.8 b/sshd.8
106index c5f8987d2..730520231 100644
107--- a/sshd.8
108+++ b/sshd.8
109@@ -893,6 +893,12 @@ the user's home directory becomes accessible.
110 This file should be writable only by the user, and need not be
111 readable by anyone else.
112 .Pp
113+.It Pa /etc/hosts.allow
114+.It Pa /etc/hosts.deny
115+Access controls that should be enforced by tcp-wrappers are defined here.
116+Further details are described in
117+.Xr hosts_access 5 .
118+.Pp
119 .It Pa /etc/hosts.equiv
120 This file is for host-based authentication (see
121 .Xr ssh 1 ) .
122@@ -995,6 +1001,7 @@ The content of this file is not sensitive; it can be world-readable.
123 .Xr ssh-keygen 1 ,
124 .Xr ssh-keyscan 1 ,
125 .Xr chroot 2 ,
126+.Xr hosts_access 5 ,
127 .Xr login.conf 5 ,
128 .Xr moduli 5 ,
129 .Xr sshd_config 5 ,
130diff --git a/sshd.c b/sshd.c
131index d92f03aaf..62dc55cf2 100644
132--- a/sshd.c
133+++ b/sshd.c
134@@ -124,6 +124,13 @@
135 #include "ssherr.h"
136 #include "sk-api.h"
137
138+#ifdef LIBWRAP
139+#include <tcpd.h>
140+#include <syslog.h>
141+int allow_severity;
142+int deny_severity;
143+#endif /* LIBWRAP */
144+
145 /* Re-exec fds */
146 #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
147 #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2)
148@@ -2138,6 +2145,24 @@ main(int ac, char **av)
149 #ifdef SSH_AUDIT_EVENTS
150 audit_connection_from(remote_ip, remote_port);
151 #endif
152+#ifdef LIBWRAP
153+ allow_severity = options.log_facility|LOG_INFO;
154+ deny_severity = options.log_facility|LOG_WARNING;
155+ /* Check whether logins are denied from this host. */
156+ if (ssh_packet_connection_is_on_socket(ssh)) {
157+ struct request_info req;
158+
159+ request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0);
160+ fromhost(&req);
161+
162+ if (!hosts_access(&req)) {
163+ debug("Connection refused by tcp wrapper");
164+ refuse(&req);
165+ /* NOTREACHED */
166+ fatal("libwrap refuse returns");
167+ }
168+ }
169+#endif /* LIBWRAP */
170
171 rdomain = ssh_packet_rdomain_in(ssh);
172
diff --git a/debian/patches/revert-ipqos-defaults.patch b/debian/patches/revert-ipqos-defaults.patch
new file mode 100644
index 000000000..02c505531
--- /dev/null
+++ b/debian/patches/revert-ipqos-defaults.patch
@@ -0,0 +1,93 @@
1From a2dabf35ce0228c86a288d11cc847a9d9801604f Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Mon, 8 Apr 2019 10:46:29 +0100
4Subject: Revert "upstream: Update default IPQoS in ssh(1), sshd(8) to DSCP
5 AF21 for"
6
7This reverts commit 5ee8448ad7c306f05a9f56769f95336a8269f379.
8
9The IPQoS default changes have some unfortunate interactions with
10iptables (see https://bugs.debian.org/923880) and VMware, so I'm
11temporarily reverting them until those have been fixed.
12
13Bug-Debian: https://bugs.debian.org/923879
14Bug-Debian: https://bugs.debian.org/926229
15Bug-Ubuntu: https://bugs.launchpad.net/bugs/1822370
16Last-Update: 2019-04-08
17
18Patch-Name: revert-ipqos-defaults.patch
19---
20 readconf.c | 4 ++--
21 servconf.c | 4 ++--
22 ssh_config.5 | 6 ++----
23 sshd_config.5 | 6 ++----
24 4 files changed, 8 insertions(+), 12 deletions(-)
25
26diff --git a/readconf.c b/readconf.c
27index e82024678..1b9494d7c 100644
28--- a/readconf.c
29+++ b/readconf.c
30@@ -2230,9 +2230,9 @@ fill_default_options(Options * options)
31 if (options->visual_host_key == -1)
32 options->visual_host_key = 0;
33 if (options->ip_qos_interactive == -1)
34- options->ip_qos_interactive = IPTOS_DSCP_AF21;
35+ options->ip_qos_interactive = IPTOS_LOWDELAY;
36 if (options->ip_qos_bulk == -1)
37- options->ip_qos_bulk = IPTOS_DSCP_CS1;
38+ options->ip_qos_bulk = IPTOS_THROUGHPUT;
39 if (options->request_tty == -1)
40 options->request_tty = REQUEST_TTY_AUTO;
41 if (options->proxy_use_fdpass == -1)
42diff --git a/servconf.c b/servconf.c
43index 7bbc25c2e..470ad3619 100644
44--- a/servconf.c
45+++ b/servconf.c
46@@ -452,9 +452,9 @@ fill_default_server_options(ServerOptions *options)
47 if (options->permit_tun == -1)
48 options->permit_tun = SSH_TUNMODE_NO;
49 if (options->ip_qos_interactive == -1)
50- options->ip_qos_interactive = IPTOS_DSCP_AF21;
51+ options->ip_qos_interactive = IPTOS_LOWDELAY;
52 if (options->ip_qos_bulk == -1)
53- options->ip_qos_bulk = IPTOS_DSCP_CS1;
54+ options->ip_qos_bulk = IPTOS_THROUGHPUT;
55 if (options->version_addendum == NULL)
56 options->version_addendum = xstrdup("");
57 if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
58diff --git a/ssh_config.5 b/ssh_config.5
59index 5c90d3e02..6b4e4f43b 100644
60--- a/ssh_config.5
61+++ b/ssh_config.5
62@@ -1133,11 +1133,9 @@ If one argument is specified, it is used as the packet class unconditionally.
63 If two values are specified, the first is automatically selected for
64 interactive sessions and the second for non-interactive sessions.
65 The default is
66-.Cm af21
67-(Low-Latency Data)
68+.Cm lowdelay
69 for interactive sessions and
70-.Cm cs1
71-(Lower Effort)
72+.Cm throughput
73 for non-interactive sessions.
74 .It Cm KbdInteractiveAuthentication
75 Specifies whether to use keyboard-interactive authentication.
76diff --git a/sshd_config.5 b/sshd_config.5
77index b8bea2ad7..fd205e418 100644
78--- a/sshd_config.5
79+++ b/sshd_config.5
80@@ -907,11 +907,9 @@ If one argument is specified, it is used as the packet class unconditionally.
81 If two values are specified, the first is automatically selected for
82 interactive sessions and the second for non-interactive sessions.
83 The default is
84-.Cm af21
85-(Low-Latency Data)
86+.Cm lowdelay
87 for interactive sessions and
88-.Cm cs1
89-(Lower Effort)
90+.Cm throughput
91 for non-interactive sessions.
92 .It Cm KbdInteractiveAuthentication
93 Specifies whether to allow keyboard-interactive authentication.
diff --git a/debian/patches/scp-quoting.patch b/debian/patches/scp-quoting.patch
new file mode 100644
index 000000000..8935b8e04
--- /dev/null
+++ b/debian/patches/scp-quoting.patch
@@ -0,0 +1,41 @@
1From 5166a6af68da4778c7e2c2d117bb56361c7aa361 Mon Sep 17 00:00:00 2001
2From: =?UTF-8?q?Nicolas=20Valc=C3=A1rcel?= <nvalcarcel@ubuntu.com>
3Date: Sun, 9 Feb 2014 16:09:59 +0000
4Subject: Adjust scp quoting in verbose mode
5
6Tweak scp's reporting of filenames in verbose mode to be a bit less
7confusing with spaces.
8
9This should be revised to mimic real shell quoting.
10
11Bug-Ubuntu: https://bugs.launchpad.net/bugs/89945
12Last-Update: 2010-02-27
13
14Patch-Name: scp-quoting.patch
15---
16 scp.c | 12 ++++++++++--
17 1 file changed, 10 insertions(+), 2 deletions(-)
18
19diff --git a/scp.c b/scp.c
20index 6901e0c94..9b64aa5f4 100644
21--- a/scp.c
22+++ b/scp.c
23@@ -201,8 +201,16 @@ do_local_cmd(arglist *a)
24
25 if (verbose_mode) {
26 fprintf(stderr, "Executing:");
27- for (i = 0; i < a->num; i++)
28- fmprintf(stderr, " %s", a->list[i]);
29+ for (i = 0; i < a->num; i++) {
30+ if (i == 0)
31+ fmprintf(stderr, " %s", a->list[i]);
32+ else
33+ /*
34+ * TODO: misbehaves if a->list[i] contains a
35+ * single quote
36+ */
37+ fmprintf(stderr, " '%s'", a->list[i]);
38+ }
39 fprintf(stderr, "\n");
40 }
41 if ((pid = fork()) == -1)
diff --git a/debian/patches/selinux-role.patch b/debian/patches/selinux-role.patch
new file mode 100644
index 000000000..63e44af55
--- /dev/null
+++ b/debian/patches/selinux-role.patch
@@ -0,0 +1,472 @@
1From b108c6bbe4b3691600a272b27fa24d9080018db7 Mon Sep 17 00:00:00 2001
2From: Manoj Srivastava <srivasta@debian.org>
3Date: Sun, 9 Feb 2014 16:09:49 +0000
4Subject: Handle SELinux authorisation roles
5
6Rejected upstream due to discomfort with magic usernames; a better approach
7will need an SSH protocol change. In the meantime, this came from Debian's
8SELinux maintainer, so we'll keep it until we have something better.
9
10Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1641
11Bug-Debian: http://bugs.debian.org/394795
12Last-Update: 2020-02-21
13
14Patch-Name: selinux-role.patch
15---
16 auth.h | 1 +
17 auth2.c | 10 ++++++++--
18 monitor.c | 37 +++++++++++++++++++++++++++++++++----
19 monitor.h | 2 ++
20 monitor_wrap.c | 27 ++++++++++++++++++++++++---
21 monitor_wrap.h | 3 ++-
22 openbsd-compat/port-linux.c | 21 ++++++++++++++-------
23 openbsd-compat/port-linux.h | 4 ++--
24 platform.c | 4 ++--
25 platform.h | 2 +-
26 session.c | 10 +++++-----
27 session.h | 2 +-
28 sshd.c | 2 +-
29 sshpty.c | 4 ++--
30 sshpty.h | 2 +-
31 15 files changed, 99 insertions(+), 32 deletions(-)
32
33diff --git a/auth.h b/auth.h
34index becc672b5..5da9fe75f 100644
35--- a/auth.h
36+++ b/auth.h
37@@ -63,6 +63,7 @@ struct Authctxt {
38 char *service;
39 struct passwd *pw; /* set if 'valid' */
40 char *style;
41+ char *role;
42
43 /* Method lists for multiple authentication */
44 char **auth_methods; /* modified from server config */
45diff --git a/auth2.c b/auth2.c
46index 1c217268c..92a6bcaf4 100644
47--- a/auth2.c
48+++ b/auth2.c
49@@ -265,7 +265,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
50 {
51 Authctxt *authctxt = ssh->authctxt;
52 Authmethod *m = NULL;
53- char *user = NULL, *service = NULL, *method = NULL, *style = NULL;
54+ char *user = NULL, *service = NULL, *method = NULL, *style = NULL, *role = NULL;
55 int r, authenticated = 0;
56 double tstart = monotime_double();
57
58@@ -279,8 +279,13 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
59 debug("userauth-request for user %s service %s method %s", user, service, method);
60 debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
61
62+ if ((role = strchr(user, '/')) != NULL)
63+ *role++ = 0;
64+
65 if ((style = strchr(user, ':')) != NULL)
66 *style++ = 0;
67+ else if (role && (style = strchr(role, ':')) != NULL)
68+ *style++ = '\0';
69
70 if (authctxt->attempt++ == 0) {
71 /* setup auth context */
72@@ -307,8 +312,9 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
73 use_privsep ? " [net]" : "");
74 authctxt->service = xstrdup(service);
75 authctxt->style = style ? xstrdup(style) : NULL;
76+ authctxt->role = role ? xstrdup(role) : NULL;
77 if (use_privsep)
78- mm_inform_authserv(service, style);
79+ mm_inform_authserv(service, style, role);
80 userauth_banner(ssh);
81 if (auth2_setup_methods_lists(authctxt) != 0)
82 ssh_packet_disconnect(ssh,
83diff --git a/monitor.c b/monitor.c
84index ebf76c7f9..947fdfadc 100644
85--- a/monitor.c
86+++ b/monitor.c
87@@ -118,6 +118,7 @@ int mm_answer_sign(struct ssh *, int, struct sshbuf *);
88 int mm_answer_pwnamallow(struct ssh *, int, struct sshbuf *);
89 int mm_answer_auth2_read_banner(struct ssh *, int, struct sshbuf *);
90 int mm_answer_authserv(struct ssh *, int, struct sshbuf *);
91+int mm_answer_authrole(struct ssh *, int, struct sshbuf *);
92 int mm_answer_authpassword(struct ssh *, int, struct sshbuf *);
93 int mm_answer_bsdauthquery(struct ssh *, int, struct sshbuf *);
94 int mm_answer_bsdauthrespond(struct ssh *, int, struct sshbuf *);
95@@ -198,6 +199,7 @@ struct mon_table mon_dispatch_proto20[] = {
96 {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
97 {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
98 {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
99+ {MONITOR_REQ_AUTHROLE, MON_ONCE, mm_answer_authrole},
100 {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
101 {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
102 #ifdef USE_PAM
103@@ -820,6 +822,7 @@ mm_answer_pwnamallow(struct ssh *ssh, int sock, struct sshbuf *m)
104
105 /* Allow service/style information on the auth context */
106 monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1);
107+ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHROLE, 1);
108 monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1);
109
110 #ifdef USE_PAM
111@@ -853,16 +856,42 @@ mm_answer_authserv(struct ssh *ssh, int sock, struct sshbuf *m)
112 monitor_permit_authentications(1);
113
114 if ((r = sshbuf_get_cstring(m, &authctxt->service, NULL)) != 0 ||
115- (r = sshbuf_get_cstring(m, &authctxt->style, NULL)) != 0)
116+ (r = sshbuf_get_cstring(m, &authctxt->style, NULL)) != 0 ||
117+ (r = sshbuf_get_cstring(m, &authctxt->role, NULL)) != 0)
118 fatal("%s: buffer error: %s", __func__, ssh_err(r));
119- debug3("%s: service=%s, style=%s",
120- __func__, authctxt->service, authctxt->style);
121+ debug3("%s: service=%s, style=%s, role=%s",
122+ __func__, authctxt->service, authctxt->style, authctxt->role);
123
124 if (strlen(authctxt->style) == 0) {
125 free(authctxt->style);
126 authctxt->style = NULL;
127 }
128
129+ if (strlen(authctxt->role) == 0) {
130+ free(authctxt->role);
131+ authctxt->role = NULL;
132+ }
133+
134+ return (0);
135+}
136+
137+int
138+mm_answer_authrole(struct ssh *ssh, int sock, struct sshbuf *m)
139+{
140+ int r;
141+
142+ monitor_permit_authentications(1);
143+
144+ if ((r = sshbuf_get_cstring(m, &authctxt->role, NULL)) != 0)
145+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
146+ debug3("%s: role=%s",
147+ __func__, authctxt->role);
148+
149+ if (strlen(authctxt->role) == 0) {
150+ free(authctxt->role);
151+ authctxt->role = NULL;
152+ }
153+
154 return (0);
155 }
156
157@@ -1554,7 +1583,7 @@ mm_answer_pty(struct ssh *ssh, int sock, struct sshbuf *m)
158 res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty));
159 if (res == 0)
160 goto error;
161- pty_setowner(authctxt->pw, s->tty);
162+ pty_setowner(authctxt->pw, s->tty, authctxt->role);
163
164 if ((r = sshbuf_put_u32(m, 1)) != 0 ||
165 (r = sshbuf_put_cstring(m, s->tty)) != 0)
166diff --git a/monitor.h b/monitor.h
167index 2b1a2d590..4d87284aa 100644
168--- a/monitor.h
169+++ b/monitor.h
170@@ -65,6 +65,8 @@ enum monitor_reqtype {
171
172 MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
173 MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
174+
175+ MONITOR_REQ_AUTHROLE = 154,
176 };
177
178 struct ssh;
179diff --git a/monitor_wrap.c b/monitor_wrap.c
180index 6edb509a3..b49c268d3 100644
181--- a/monitor_wrap.c
182+++ b/monitor_wrap.c
183@@ -364,10 +364,10 @@ mm_auth2_read_banner(void)
184 return (banner);
185 }
186
187-/* Inform the privileged process about service and style */
188+/* Inform the privileged process about service, style, and role */
189
190 void
191-mm_inform_authserv(char *service, char *style)
192+mm_inform_authserv(char *service, char *style, char *role)
193 {
194 struct sshbuf *m;
195 int r;
196@@ -377,7 +377,8 @@ mm_inform_authserv(char *service, char *style)
197 if ((m = sshbuf_new()) == NULL)
198 fatal("%s: sshbuf_new failed", __func__);
199 if ((r = sshbuf_put_cstring(m, service)) != 0 ||
200- (r = sshbuf_put_cstring(m, style ? style : "")) != 0)
201+ (r = sshbuf_put_cstring(m, style ? style : "")) != 0 ||
202+ (r = sshbuf_put_cstring(m, role ? role : "")) != 0)
203 fatal("%s: buffer error: %s", __func__, ssh_err(r));
204
205 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHSERV, m);
206@@ -385,6 +386,26 @@ mm_inform_authserv(char *service, char *style)
207 sshbuf_free(m);
208 }
209
210+/* Inform the privileged process about role */
211+
212+void
213+mm_inform_authrole(char *role)
214+{
215+ struct sshbuf *m;
216+ int r;
217+
218+ debug3("%s entering", __func__);
219+
220+ if ((m = sshbuf_new()) == NULL)
221+ fatal("%s: sshbuf_new failed", __func__);
222+ if ((r = sshbuf_put_cstring(m, role ? role : "")) != 0)
223+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
224+
225+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHROLE, m);
226+
227+ sshbuf_free(m);
228+}
229+
230 /* Do the password authentication */
231 int
232 mm_auth_password(struct ssh *ssh, char *password)
233diff --git a/monitor_wrap.h b/monitor_wrap.h
234index 485590c18..370b08e17 100644
235--- a/monitor_wrap.h
236+++ b/monitor_wrap.h
237@@ -47,7 +47,8 @@ DH *mm_choose_dh(int, int, int);
238 #endif
239 int mm_sshkey_sign(struct ssh *, struct sshkey *, u_char **, size_t *,
240 const u_char *, size_t, const char *, const char *, u_int compat);
241-void mm_inform_authserv(char *, char *);
242+void mm_inform_authserv(char *, char *, char *);
243+void mm_inform_authrole(char *);
244 struct passwd *mm_getpwnamallow(struct ssh *, const char *);
245 char *mm_auth2_read_banner(void);
246 int mm_auth_password(struct ssh *, char *);
247diff --git a/openbsd-compat/port-linux.c b/openbsd-compat/port-linux.c
248index 622988822..3e6e07670 100644
249--- a/openbsd-compat/port-linux.c
250+++ b/openbsd-compat/port-linux.c
251@@ -56,7 +56,7 @@ ssh_selinux_enabled(void)
252
253 /* Return the default security context for the given username */
254 static security_context_t
255-ssh_selinux_getctxbyname(char *pwname)
256+ssh_selinux_getctxbyname(char *pwname, const char *role)
257 {
258 security_context_t sc = NULL;
259 char *sename = NULL, *lvl = NULL;
260@@ -71,9 +71,16 @@ ssh_selinux_getctxbyname(char *pwname)
261 #endif
262
263 #ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL
264- r = get_default_context_with_level(sename, lvl, NULL, &sc);
265+ if (role != NULL && role[0])
266+ r = get_default_context_with_rolelevel(sename, role, lvl, NULL,
267+ &sc);
268+ else
269+ r = get_default_context_with_level(sename, lvl, NULL, &sc);
270 #else
271- r = get_default_context(sename, NULL, &sc);
272+ if (role != NULL && role[0])
273+ r = get_default_context_with_role(sename, role, NULL, &sc);
274+ else
275+ r = get_default_context(sename, NULL, &sc);
276 #endif
277
278 if (r != 0) {
279@@ -103,7 +110,7 @@ ssh_selinux_getctxbyname(char *pwname)
280
281 /* Set the execution context to the default for the specified user */
282 void
283-ssh_selinux_setup_exec_context(char *pwname)
284+ssh_selinux_setup_exec_context(char *pwname, const char *role)
285 {
286 security_context_t user_ctx = NULL;
287
288@@ -112,7 +119,7 @@ ssh_selinux_setup_exec_context(char *pwname)
289
290 debug3("%s: setting execution context", __func__);
291
292- user_ctx = ssh_selinux_getctxbyname(pwname);
293+ user_ctx = ssh_selinux_getctxbyname(pwname, role);
294 if (setexeccon(user_ctx) != 0) {
295 switch (security_getenforce()) {
296 case -1:
297@@ -134,7 +141,7 @@ ssh_selinux_setup_exec_context(char *pwname)
298
299 /* Set the TTY context for the specified user */
300 void
301-ssh_selinux_setup_pty(char *pwname, const char *tty)
302+ssh_selinux_setup_pty(char *pwname, const char *tty, const char *role)
303 {
304 security_context_t new_tty_ctx = NULL;
305 security_context_t user_ctx = NULL;
306@@ -146,7 +153,7 @@ ssh_selinux_setup_pty(char *pwname, const char *tty)
307
308 debug3("%s: setting TTY context on %s", __func__, tty);
309
310- user_ctx = ssh_selinux_getctxbyname(pwname);
311+ user_ctx = ssh_selinux_getctxbyname(pwname, role);
312
313 /* XXX: should these calls fatal() upon failure in enforcing mode? */
314
315diff --git a/openbsd-compat/port-linux.h b/openbsd-compat/port-linux.h
316index 3c22a854d..c88129428 100644
317--- a/openbsd-compat/port-linux.h
318+++ b/openbsd-compat/port-linux.h
319@@ -19,8 +19,8 @@
320
321 #ifdef WITH_SELINUX
322 int ssh_selinux_enabled(void);
323-void ssh_selinux_setup_pty(char *, const char *);
324-void ssh_selinux_setup_exec_context(char *);
325+void ssh_selinux_setup_pty(char *, const char *, const char *);
326+void ssh_selinux_setup_exec_context(char *, const char *);
327 void ssh_selinux_change_context(const char *);
328 void ssh_selinux_setfscreatecon(const char *);
329 #endif
330diff --git a/platform.c b/platform.c
331index 44ba71dc5..2defe9425 100644
332--- a/platform.c
333+++ b/platform.c
334@@ -143,7 +143,7 @@ platform_setusercontext(struct passwd *pw)
335 * called if sshd is running as root.
336 */
337 void
338-platform_setusercontext_post_groups(struct passwd *pw)
339+platform_setusercontext_post_groups(struct passwd *pw, const char *role)
340 {
341 #if !defined(HAVE_LOGIN_CAP) && defined(USE_PAM)
342 /*
343@@ -184,7 +184,7 @@ platform_setusercontext_post_groups(struct passwd *pw)
344 }
345 #endif /* HAVE_SETPCRED */
346 #ifdef WITH_SELINUX
347- ssh_selinux_setup_exec_context(pw->pw_name);
348+ ssh_selinux_setup_exec_context(pw->pw_name, role);
349 #endif
350 }
351
352diff --git a/platform.h b/platform.h
353index ea4f9c584..60d72ffe7 100644
354--- a/platform.h
355+++ b/platform.h
356@@ -25,7 +25,7 @@ void platform_post_fork_parent(pid_t child_pid);
357 void platform_post_fork_child(void);
358 int platform_privileged_uidswap(void);
359 void platform_setusercontext(struct passwd *);
360-void platform_setusercontext_post_groups(struct passwd *);
361+void platform_setusercontext_post_groups(struct passwd *, const char *);
362 char *platform_get_krb5_client(const char *);
363 char *platform_krb5_get_principal_name(const char *);
364 int platform_sys_dir_uid(uid_t);
365diff --git a/session.c b/session.c
366index 06a33442a..871799590 100644
367--- a/session.c
368+++ b/session.c
369@@ -1360,7 +1360,7 @@ safely_chroot(const char *path, uid_t uid)
370
371 /* Set login name, uid, gid, and groups. */
372 void
373-do_setusercontext(struct passwd *pw)
374+do_setusercontext(struct passwd *pw, const char *role)
375 {
376 char uidstr[32], *chroot_path, *tmp;
377
378@@ -1388,7 +1388,7 @@ do_setusercontext(struct passwd *pw)
379 endgrent();
380 #endif
381
382- platform_setusercontext_post_groups(pw);
383+ platform_setusercontext_post_groups(pw, role);
384
385 if (!in_chroot && options.chroot_directory != NULL &&
386 strcasecmp(options.chroot_directory, "none") != 0) {
387@@ -1529,7 +1529,7 @@ do_child(struct ssh *ssh, Session *s, const char *command)
388
389 /* Force a password change */
390 if (s->authctxt->force_pwchange) {
391- do_setusercontext(pw);
392+ do_setusercontext(pw, s->authctxt->role);
393 child_close_fds(ssh);
394 do_pwchange(s);
395 exit(1);
396@@ -1547,7 +1547,7 @@ do_child(struct ssh *ssh, Session *s, const char *command)
397 /* When PAM is enabled we rely on it to do the nologin check */
398 if (!options.use_pam)
399 do_nologin(pw);
400- do_setusercontext(pw);
401+ do_setusercontext(pw, s->authctxt->role);
402 /*
403 * PAM session modules in do_setusercontext may have
404 * generated messages, so if this in an interactive
405@@ -1946,7 +1946,7 @@ session_pty_req(struct ssh *ssh, Session *s)
406 sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
407
408 if (!use_privsep)
409- pty_setowner(s->pw, s->tty);
410+ pty_setowner(s->pw, s->tty, s->authctxt->role);
411
412 /* Set window size from the packet. */
413 pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
414diff --git a/session.h b/session.h
415index ce59dabd9..675c91146 100644
416--- a/session.h
417+++ b/session.h
418@@ -77,7 +77,7 @@ void session_pty_cleanup2(Session *);
419 Session *session_new(void);
420 Session *session_by_tty(char *);
421 void session_close(struct ssh *, Session *);
422-void do_setusercontext(struct passwd *);
423+void do_setusercontext(struct passwd *, const char *);
424
425 const char *session_get_remote_name_or_ip(struct ssh *, u_int, int);
426
427diff --git a/sshd.c b/sshd.c
428index 62dc55cf2..65916fc6d 100644
429--- a/sshd.c
430+++ b/sshd.c
431@@ -595,7 +595,7 @@ privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
432 reseed_prngs();
433
434 /* Drop privileges */
435- do_setusercontext(authctxt->pw);
436+ do_setusercontext(authctxt->pw, authctxt->role);
437
438 skip:
439 /* It is safe now to apply the key state */
440diff --git a/sshpty.c b/sshpty.c
441index bce09e255..308449b37 100644
442--- a/sshpty.c
443+++ b/sshpty.c
444@@ -162,7 +162,7 @@ pty_change_window_size(int ptyfd, u_int row, u_int col,
445 }
446
447 void
448-pty_setowner(struct passwd *pw, const char *tty)
449+pty_setowner(struct passwd *pw, const char *tty, const char *role)
450 {
451 struct group *grp;
452 gid_t gid;
453@@ -186,7 +186,7 @@ pty_setowner(struct passwd *pw, const char *tty)
454 strerror(errno));
455
456 #ifdef WITH_SELINUX
457- ssh_selinux_setup_pty(pw->pw_name, tty);
458+ ssh_selinux_setup_pty(pw->pw_name, tty, role);
459 #endif
460
461 if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
462diff --git a/sshpty.h b/sshpty.h
463index 9ec7e9a15..de7e000ae 100644
464--- a/sshpty.h
465+++ b/sshpty.h
466@@ -24,5 +24,5 @@ int pty_allocate(int *, int *, char *, size_t);
467 void pty_release(const char *);
468 void pty_make_controlling_tty(int *, const char *);
469 void pty_change_window_size(int, u_int, u_int, u_int, u_int);
470-void pty_setowner(struct passwd *, const char *);
471+void pty_setowner(struct passwd *, const char *, const char *);
472 void disconnect_controlling_tty(void);
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 000000000..8c1046a74
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,25 @@
1gssapi.patch
2restore-tcp-wrappers.patch
3selinux-role.patch
4ssh-vulnkey-compat.patch
5keepalive-extensions.patch
6syslog-level-silent.patch
7user-group-modes.patch
8scp-quoting.patch
9shell-path.patch
10dnssec-sshfp.patch
11mention-ssh-keygen-on-keychange.patch
12package-versioning.patch
13debian-banner.patch
14authorized-keys-man-symlink.patch
15openbsd-docs.patch
16ssh-argv0.patch
17doc-hash-tab-completion.patch
18ssh-agent-setgid.patch
19no-openssl-version-status.patch
20gnome-ssh-askpass2-icon.patch
21systemd-readiness.patch
22debian-config.patch
23restore-authorized_keys2.patch
24conch-old-privkey-format.patch
25revert-ipqos-defaults.patch
diff --git a/debian/patches/shell-path.patch b/debian/patches/shell-path.patch
new file mode 100644
index 000000000..43fb1d145
--- /dev/null
+++ b/debian/patches/shell-path.patch
@@ -0,0 +1,39 @@
1From c19bcc02b07b450d585d0fd10ccd96174aeb3b7c Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 9 Feb 2014 16:10:00 +0000
4Subject: Look for $SHELL on the path for ProxyCommand/LocalCommand
5
6There's some debate on the upstream bug about whether POSIX requires this.
7I (Colin Watson) agree with Vincent and think it does.
8
9Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1494
10Bug-Debian: http://bugs.debian.org/492728
11Last-Update: 2020-02-21
12
13Patch-Name: shell-path.patch
14---
15 sshconnect.c | 4 ++--
16 1 file changed, 2 insertions(+), 2 deletions(-)
17
18diff --git a/sshconnect.c b/sshconnect.c
19index 4711af782..4a5d4a003 100644
20--- a/sshconnect.c
21+++ b/sshconnect.c
22@@ -260,7 +260,7 @@ ssh_proxy_connect(struct ssh *ssh, const char *host, const char *host_arg,
23 /* Execute the proxy command. Note that we gave up any
24 extra privileges above. */
25 ssh_signal(SIGPIPE, SIG_DFL);
26- execv(argv[0], argv);
27+ execvp(argv[0], argv);
28 perror(argv[0]);
29 exit(1);
30 }
31@@ -1388,7 +1388,7 @@ ssh_local_cmd(const char *args)
32 if (pid == 0) {
33 ssh_signal(SIGPIPE, SIG_DFL);
34 debug3("Executing %s -c \"%s\"", shell, args);
35- execl(shell, shell, "-c", args, (char *)NULL);
36+ execlp(shell, shell, "-c", args, (char *)NULL);
37 error("Couldn't execute %s -c \"%s\": %s",
38 shell, args, strerror(errno));
39 _exit(1);
diff --git a/debian/patches/ssh-agent-setgid.patch b/debian/patches/ssh-agent-setgid.patch
new file mode 100644
index 000000000..e7849e6c3
--- /dev/null
+++ b/debian/patches/ssh-agent-setgid.patch
@@ -0,0 +1,40 @@
1From ad09303388f0172ab6e028aaf27d87cf873d123d Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 9 Feb 2014 16:10:13 +0000
4Subject: Document consequences of ssh-agent being setgid in ssh-agent(1)
5
6Bug-Debian: http://bugs.debian.org/711623
7Forwarded: no
8Last-Update: 2020-02-21
9
10Patch-Name: ssh-agent-setgid.patch
11---
12 ssh-agent.1 | 15 +++++++++++++++
13 1 file changed, 15 insertions(+)
14
15diff --git a/ssh-agent.1 b/ssh-agent.1
16index fff0db6bc..99e4f6d2e 100644
17--- a/ssh-agent.1
18+++ b/ssh-agent.1
19@@ -201,6 +201,21 @@ socket and stores its pathname in this variable.
20 It is accessible only to the current user,
21 but is easily abused by root or another instance of the same user.
22 .El
23+.Pp
24+In Debian,
25+.Nm
26+is installed with the set-group-id bit set, to prevent
27+.Xr ptrace 2
28+attacks retrieving private key material.
29+This has the side-effect of causing the run-time linker to remove certain
30+environment variables which might have security implications for set-id
31+programs, including
32+.Ev LD_PRELOAD ,
33+.Ev LD_LIBRARY_PATH ,
34+and
35+.Ev TMPDIR .
36+If you need to set any of these environment variables, you will need to do
37+so in the program executed by ssh-agent.
38 .Sh FILES
39 .Bl -tag -width Ds
40 .It Pa $TMPDIR/ssh-XXXXXXXXXX/agent.<ppid>
diff --git a/debian/patches/ssh-argv0.patch b/debian/patches/ssh-argv0.patch
new file mode 100644
index 000000000..8f796719d
--- /dev/null
+++ b/debian/patches/ssh-argv0.patch
@@ -0,0 +1,31 @@
1From 4b1e0000a099f988553ccc4b274e1790b5114c12 Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 9 Feb 2014 16:10:10 +0000
4Subject: ssh(1): Refer to ssh-argv0(1)
5
6Old versions of OpenSSH (up to 2.5 or thereabouts) allowed creating symlinks
7to ssh with the name of the host you want to connect to. Debian ships an
8ssh-argv0 script restoring this feature; this patch refers to its manual
9page from ssh(1).
10
11Bug-Debian: http://bugs.debian.org/111341
12Forwarded: not-needed
13Last-Update: 2013-09-14
14
15Patch-Name: ssh-argv0.patch
16---
17 ssh.1 | 1 +
18 1 file changed, 1 insertion(+)
19
20diff --git a/ssh.1 b/ssh.1
21index 17b0e984f..b33a8049f 100644
22--- a/ssh.1
23+++ b/ssh.1
24@@ -1610,6 +1610,7 @@ if an error occurred.
25 .Xr sftp 1 ,
26 .Xr ssh-add 1 ,
27 .Xr ssh-agent 1 ,
28+.Xr ssh-argv0 1 ,
29 .Xr ssh-keygen 1 ,
30 .Xr ssh-keyscan 1 ,
31 .Xr tun 4 ,
diff --git a/debian/patches/ssh-vulnkey-compat.patch b/debian/patches/ssh-vulnkey-compat.patch
new file mode 100644
index 000000000..99116e9c4
--- /dev/null
+++ b/debian/patches/ssh-vulnkey-compat.patch
@@ -0,0 +1,42 @@
1From 11d571f137c76d8c2e38b1c1a537b04cc279f8e3 Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@ubuntu.com>
3Date: Sun, 9 Feb 2014 16:09:50 +0000
4Subject: Accept obsolete ssh-vulnkey configuration options
5
6These options were used as part of Debian's response to CVE-2008-0166.
7Nearly six years later, we no longer need to continue carrying the bulk
8of that patch, but we do need to avoid failing when the associated
9configuration options are still present.
10
11Last-Update: 2014-02-09
12
13Patch-Name: ssh-vulnkey-compat.patch
14---
15 readconf.c | 1 +
16 servconf.c | 1 +
17 2 files changed, 2 insertions(+)
18
19diff --git a/readconf.c b/readconf.c
20index da8022dd0..0fc996871 100644
21--- a/readconf.c
22+++ b/readconf.c
23@@ -191,6 +191,7 @@ static struct {
24 { "fallbacktorsh", oDeprecated },
25 { "globalknownhostsfile2", oDeprecated },
26 { "rhostsauthentication", oDeprecated },
27+ { "useblacklistedkeys", oDeprecated },
28 { "userknownhostsfile2", oDeprecated },
29 { "useroaming", oDeprecated },
30 { "usersh", oDeprecated },
31diff --git a/servconf.c b/servconf.c
32index 191575a16..bf3cd84a4 100644
33--- a/servconf.c
34+++ b/servconf.c
35@@ -656,6 +656,7 @@ static struct {
36 { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
37 { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL },
38 { "strictmodes", sStrictModes, SSHCFG_GLOBAL },
39+ { "permitblacklistedkeys", sDeprecated, SSHCFG_GLOBAL },
40 { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL },
41 { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL },
42 { "uselogin", sDeprecated, SSHCFG_GLOBAL },
diff --git a/debian/patches/syslog-level-silent.patch b/debian/patches/syslog-level-silent.patch
new file mode 100644
index 000000000..234d95ad2
--- /dev/null
+++ b/debian/patches/syslog-level-silent.patch
@@ -0,0 +1,47 @@
1From 387c2c1954773733bae9fca21a92db62c31180bd Mon Sep 17 00:00:00 2001
2From: Natalie Amery <nmamery@chiark.greenend.org.uk>
3Date: Sun, 9 Feb 2014 16:09:54 +0000
4Subject: "LogLevel SILENT" compatibility
5
6"LogLevel SILENT" (-qq) was introduced in Debian openssh 1:3.0.1p1-1 to
7match the behaviour of non-free SSH, in which -q does not suppress fatal
8errors. However, this was unintentionally broken in 1:4.6p1-2 and nobody
9complained, so we've dropped most of it. The parts that remain are basic
10configuration file compatibility, and an adjustment to "Pseudo-terminal will
11not be allocated ..." which should be split out into a separate patch.
12
13Author: Matthew Vernon <matthew@debian.org>
14Author: Colin Watson <cjwatson@debian.org>
15Last-Update: 2013-09-14
16
17Patch-Name: syslog-level-silent.patch
18---
19 log.c | 1 +
20 ssh.c | 2 +-
21 2 files changed, 2 insertions(+), 1 deletion(-)
22
23diff --git a/log.c b/log.c
24index d9c2d136c..1749af6d1 100644
25--- a/log.c
26+++ b/log.c
27@@ -93,6 +93,7 @@ static struct {
28 LogLevel val;
29 } log_levels[] =
30 {
31+ { "SILENT", SYSLOG_LEVEL_QUIET }, /* compatibility */
32 { "QUIET", SYSLOG_LEVEL_QUIET },
33 { "FATAL", SYSLOG_LEVEL_FATAL },
34 { "ERROR", SYSLOG_LEVEL_ERROR },
35diff --git a/ssh.c b/ssh.c
36index 110cf9c19..6138fd4d3 100644
37--- a/ssh.c
38+++ b/ssh.c
39@@ -1305,7 +1305,7 @@ main(int ac, char **av)
40 /* Do not allocate a tty if stdin is not a tty. */
41 if ((!isatty(fileno(stdin)) || stdin_null_flag) &&
42 options.request_tty != REQUEST_TTY_FORCE) {
43- if (tty_flag)
44+ if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET)
45 logit("Pseudo-terminal will not be allocated because "
46 "stdin is not a terminal.");
47 tty_flag = 0;
diff --git a/debian/patches/systemd-readiness.patch b/debian/patches/systemd-readiness.patch
new file mode 100644
index 000000000..fdcfca30d
--- /dev/null
+++ b/debian/patches/systemd-readiness.patch
@@ -0,0 +1,84 @@
1From a208834b2d1811dac7054d7fdcdd04672f8b19f6 Mon Sep 17 00:00:00 2001
2From: Michael Biebl <biebl@debian.org>
3Date: Mon, 21 Dec 2015 16:08:47 +0000
4Subject: Add systemd readiness notification support
5
6Bug-Debian: https://bugs.debian.org/778913
7Forwarded: no
8Last-Update: 2017-08-22
9
10Patch-Name: systemd-readiness.patch
11---
12 configure.ac | 24 ++++++++++++++++++++++++
13 sshd.c | 9 +++++++++
14 2 files changed, 33 insertions(+)
15
16diff --git a/configure.ac b/configure.ac
17index cee7cbc51..5db3013de 100644
18--- a/configure.ac
19+++ b/configure.ac
20@@ -4664,6 +4664,29 @@ AC_ARG_WITH([kerberos5],
21 AC_SUBST([GSSLIBS])
22 AC_SUBST([K5LIBS])
23
24+# Check whether user wants systemd support
25+SYSTEMD_MSG="no"
26+AC_ARG_WITH(systemd,
27+ [ --with-systemd Enable systemd support],
28+ [ if test "x$withval" != "xno" ; then
29+ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no])
30+ if test "$PKGCONFIG" != "no"; then
31+ AC_MSG_CHECKING([for libsystemd])
32+ if $PKGCONFIG --exists libsystemd; then
33+ SYSTEMD_CFLAGS=`$PKGCONFIG --cflags libsystemd`
34+ SYSTEMD_LIBS=`$PKGCONFIG --libs libsystemd`
35+ CPPFLAGS="$CPPFLAGS $SYSTEMD_CFLAGS"
36+ SSHDLIBS="$SSHDLIBS $SYSTEMD_LIBS"
37+ AC_MSG_RESULT([yes])
38+ AC_DEFINE(HAVE_SYSTEMD, 1, [Define if you want systemd support.])
39+ SYSTEMD_MSG="yes"
40+ else
41+ AC_MSG_RESULT([no])
42+ fi
43+ fi
44+ fi ]
45+)
46+
47 # Looking for programs, paths and files
48
49 PRIVSEP_PATH=/var/empty
50@@ -5476,6 +5499,7 @@ echo " libldns support: $LDNS_MSG"
51 echo " Solaris process contract support: $SPC_MSG"
52 echo " Solaris project support: $SP_MSG"
53 echo " Solaris privilege support: $SPP_MSG"
54+echo " systemd support: $SYSTEMD_MSG"
55 echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG"
56 echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG"
57 echo " BSD Auth support: $BSD_AUTH_MSG"
58diff --git a/sshd.c b/sshd.c
59index da876a900..c069505a0 100644
60--- a/sshd.c
61+++ b/sshd.c
62@@ -85,6 +85,10 @@
63 #include <prot.h>
64 #endif
65
66+#ifdef HAVE_SYSTEMD
67+#include <systemd/sd-daemon.h>
68+#endif
69+
70 #include "xmalloc.h"
71 #include "ssh.h"
72 #include "ssh2.h"
73@@ -2027,6 +2031,11 @@ main(int ac, char **av)
74 }
75 }
76
77+#ifdef HAVE_SYSTEMD
78+ /* Signal systemd that we are ready to accept connections */
79+ sd_notify(0, "READY=1");
80+#endif
81+
82 /* Accept a connection and return in a forked child */
83 server_accept_loop(&sock_in, &sock_out,
84 &newsock, config_s);
diff --git a/debian/patches/user-group-modes.patch b/debian/patches/user-group-modes.patch
new file mode 100644
index 000000000..8bd35addf
--- /dev/null
+++ b/debian/patches/user-group-modes.patch
@@ -0,0 +1,210 @@
1From 3309e464e5ae6c940ddd584eed4d2d403f4c168c Mon Sep 17 00:00:00 2001
2From: Colin Watson <cjwatson@debian.org>
3Date: Sun, 9 Feb 2014 16:09:58 +0000
4Subject: Allow harmless group-writability
5
6Allow secure files (~/.ssh/config, ~/.ssh/authorized_keys, etc.) to be
7group-writable, provided that the group in question contains only the file's
8owner. Rejected upstream for IMO incorrect reasons (e.g. a misunderstanding
9about the contents of gr->gr_mem). Given that per-user groups and umask 002
10are the default setup in Debian (for good reasons - this makes operating in
11setgid directories with other groups much easier), we need to permit this by
12default.
13
14Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1060
15Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=314347
16Last-Update: 2019-10-09
17
18Patch-Name: user-group-modes.patch
19---
20 auth-rhosts.c | 6 ++----
21 auth.c | 3 +--
22 misc.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++-----
23 misc.h | 2 ++
24 readconf.c | 3 +--
25 ssh.1 | 2 ++
26 ssh_config.5 | 2 ++
27 7 files changed, 63 insertions(+), 13 deletions(-)
28
29diff --git a/auth-rhosts.c b/auth-rhosts.c
30index 7a10210b6..587f53721 100644
31--- a/auth-rhosts.c
32+++ b/auth-rhosts.c
33@@ -260,8 +260,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
34 return 0;
35 }
36 if (options.strict_modes &&
37- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
38- (st.st_mode & 022) != 0)) {
39+ !secure_permissions(&st, pw->pw_uid)) {
40 logit("Rhosts authentication refused for %.100s: "
41 "bad ownership or modes for home directory.", pw->pw_name);
42 auth_debug_add("Rhosts authentication refused for %.100s: "
43@@ -287,8 +286,7 @@ auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
44 * allowing access to their account by anyone.
45 */
46 if (options.strict_modes &&
47- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
48- (st.st_mode & 022) != 0)) {
49+ !secure_permissions(&st, pw->pw_uid)) {
50 logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
51 pw->pw_name, buf);
52 auth_debug_add("Bad file modes for %.200s", buf);
53diff --git a/auth.c b/auth.c
54index 687c57b42..aed3c13ac 100644
55--- a/auth.c
56+++ b/auth.c
57@@ -474,8 +474,7 @@ check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host,
58 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
59 if (options.strict_modes &&
60 (stat(user_hostfile, &st) == 0) &&
61- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
62- (st.st_mode & 022) != 0)) {
63+ !secure_permissions(&st, pw->pw_uid)) {
64 logit("Authentication refused for %.100s: "
65 "bad owner or modes for %.200s",
66 pw->pw_name, user_hostfile);
67diff --git a/misc.c b/misc.c
68index 3a31d5c18..073d3be19 100644
69--- a/misc.c
70+++ b/misc.c
71@@ -61,8 +61,9 @@
72 #include <netdb.h>
73 #ifdef HAVE_PATHS_H
74 # include <paths.h>
75-#include <pwd.h>
76 #endif
77+#include <pwd.h>
78+#include <grp.h>
79 #ifdef SSH_TUN_OPENBSD
80 #include <net/if.h>
81 #endif
82@@ -1124,6 +1125,55 @@ percent_expand(const char *string, ...)
83 #undef EXPAND_MAX_KEYS
84 }
85
86+int
87+secure_permissions(struct stat *st, uid_t uid)
88+{
89+ if (!platform_sys_dir_uid(st->st_uid) && st->st_uid != uid)
90+ return 0;
91+ if ((st->st_mode & 002) != 0)
92+ return 0;
93+ if ((st->st_mode & 020) != 0) {
94+ /* If the file is group-writable, the group in question must
95+ * have exactly one member, namely the file's owner.
96+ * (Zero-member groups are typically used by setgid
97+ * binaries, and are unlikely to be suitable.)
98+ */
99+ struct passwd *pw;
100+ struct group *gr;
101+ int members = 0;
102+
103+ gr = getgrgid(st->st_gid);
104+ if (!gr)
105+ return 0;
106+
107+ /* Check primary group memberships. */
108+ while ((pw = getpwent()) != NULL) {
109+ if (pw->pw_gid == gr->gr_gid) {
110+ ++members;
111+ if (pw->pw_uid != uid)
112+ return 0;
113+ }
114+ }
115+ endpwent();
116+
117+ pw = getpwuid(st->st_uid);
118+ if (!pw)
119+ return 0;
120+
121+ /* Check supplementary group memberships. */
122+ if (gr->gr_mem[0]) {
123+ ++members;
124+ if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
125+ gr->gr_mem[1])
126+ return 0;
127+ }
128+
129+ if (!members)
130+ return 0;
131+ }
132+ return 1;
133+}
134+
135 int
136 tun_open(int tun, int mode, char **ifname)
137 {
138@@ -1909,8 +1959,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
139 snprintf(err, errlen, "%s is not a regular file", buf);
140 return -1;
141 }
142- if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) ||
143- (stp->st_mode & 022) != 0) {
144+ if (!secure_permissions(stp, uid)) {
145 snprintf(err, errlen, "bad ownership or modes for file %s",
146 buf);
147 return -1;
148@@ -1925,8 +1974,7 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
149 strlcpy(buf, cp, sizeof(buf));
150
151 if (stat(buf, &st) == -1 ||
152- (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) ||
153- (st.st_mode & 022) != 0) {
154+ !secure_permissions(&st, uid)) {
155 snprintf(err, errlen,
156 "bad ownership or modes for directory %s", buf);
157 return -1;
158diff --git a/misc.h b/misc.h
159index 4a05db2da..5db594b91 100644
160--- a/misc.h
161+++ b/misc.h
162@@ -188,6 +188,8 @@ struct notifier_ctx *notify_start(int, const char *, ...)
163 __attribute__((format(printf, 2, 3)));
164 void notify_complete(struct notifier_ctx *);
165
166+int secure_permissions(struct stat *st, uid_t uid);
167+
168 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
169 #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
170 #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
171diff --git a/readconf.c b/readconf.c
172index 2399208f8..7f251dd4a 100644
173--- a/readconf.c
174+++ b/readconf.c
175@@ -1902,8 +1902,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
176
177 if (fstat(fileno(f), &sb) == -1)
178 fatal("fstat %s: %s", filename, strerror(errno));
179- if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
180- (sb.st_mode & 022) != 0))
181+ if (!secure_permissions(&sb, getuid()))
182 fatal("Bad owner or permissions on %s", filename);
183 }
184
185diff --git a/ssh.1 b/ssh.1
186index db5c65bc7..cf991e4ee 100644
187--- a/ssh.1
188+++ b/ssh.1
189@@ -1506,6 +1506,8 @@ The file format and configuration options are described in
190 .Xr ssh_config 5 .
191 Because of the potential for abuse, this file must have strict permissions:
192 read/write for the user, and not writable by others.
193+It may be group-writable provided that the group in question contains only
194+the user.
195 .Pp
196 .It Pa ~/.ssh/environment
197 Contains additional definitions for environment variables; see
198diff --git a/ssh_config.5 b/ssh_config.5
199index 3079db19b..e61a0fd43 100644
200--- a/ssh_config.5
201+++ b/ssh_config.5
202@@ -1952,6 +1952,8 @@ The format of this file is described above.
203 This file is used by the SSH client.
204 Because of the potential for abuse, this file must have strict permissions:
205 read/write for the user, and not writable by others.
206+It may be group-writable provided that the group in question contains only
207+the user.
208 .It Pa /etc/ssh/ssh_config
209 Systemwide configuration file.
210 This file provides defaults for those