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