summaryrefslogtreecommitdiff
path: root/debian/patches
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches')
-rw-r--r--debian/patches/auth-log-verbosity.patch123
-rw-r--r--debian/patches/authorized-keys-man-symlink.patch18
-rw-r--r--debian/patches/debian-banner.patch99
-rw-r--r--debian/patches/debian-config.patch146
-rw-r--r--debian/patches/dnssec-sshfp.patch82
-rw-r--r--debian/patches/doc-hash-tab-completion.patch20
-rw-r--r--debian/patches/gnome-ssh-askpass2-icon.patch18
-rw-r--r--debian/patches/gssapi.patch3091
-rw-r--r--debian/patches/helpful-wait-terminate.patch18
-rw-r--r--debian/patches/keepalive-extensions.patch124
-rw-r--r--debian/patches/lintian-symlink-pickiness.patch23
-rw-r--r--debian/patches/openbsd-docs.patch135
-rw-r--r--debian/patches/package-versioning.patch50
-rw-r--r--debian/patches/quieter-signals.patch31
-rw-r--r--debian/patches/scp-quoting.patch32
-rw-r--r--debian/patches/selinux-role.patch482
-rw-r--r--debian/patches/series43
-rw-r--r--debian/patches/shell-path.patch30
-rw-r--r--debian/patches/ssh-argv0.patch21
-rw-r--r--debian/patches/ssh-vulnkey.patch1382
-rw-r--r--debian/patches/ssh1-keepalive.patch65
-rw-r--r--debian/patches/syslog-level-silent.patch37
-rw-r--r--debian/patches/user-group-modes.patch202
23 files changed, 6272 insertions, 0 deletions
diff --git a/debian/patches/auth-log-verbosity.patch b/debian/patches/auth-log-verbosity.patch
new file mode 100644
index 000000000..da940d9fa
--- /dev/null
+++ b/debian/patches/auth-log-verbosity.patch
@@ -0,0 +1,123 @@
1Description: Quieten logs when multiple from= restrictions are used
2Author: Colin Watson <cjwatson@debian.org>
3Bug-Debian: http://bugs.debian.org/630606
4Forwarded: no
5Last-Update: 2011-07-28
6
7Index: b/auth-options.c
8===================================================================
9--- a/auth-options.c
10+++ b/auth-options.c
11@@ -58,9 +58,20 @@
12 /* "principals=" option. */
13 char *authorized_principals = NULL;
14
15+/* Throttle log messages. */
16+int logged_from_hostip = 0;
17+int logged_cert_hostip = 0;
18+
19 extern ServerOptions options;
20
21 void
22+auth_start_parse_options(void)
23+{
24+ logged_from_hostip = 0;
25+ logged_cert_hostip = 0;
26+}
27+
28+void
29 auth_clear_options(void)
30 {
31 no_agent_forwarding_flag = 0;
32@@ -288,10 +299,13 @@
33 /* FALLTHROUGH */
34 case 0:
35 xfree(patterns);
36- logit("Authentication tried for %.100s with "
37- "correct key but not from a permitted "
38- "host (host=%.200s, ip=%.200s).",
39- pw->pw_name, remote_host, remote_ip);
40+ if (!logged_from_hostip) {
41+ logit("Authentication tried for %.100s with "
42+ "correct key but not from a permitted "
43+ "host (host=%.200s, ip=%.200s).",
44+ pw->pw_name, remote_host, remote_ip);
45+ logged_from_hostip = 1;
46+ }
47 auth_debug_add("Your host '%.200s' is not "
48 "permitted to use this key for login.",
49 remote_host);
50@@ -526,11 +540,14 @@
51 break;
52 case 0:
53 /* no match */
54- logit("Authentication tried for %.100s "
55- "with valid certificate but not "
56- "from a permitted host "
57- "(ip=%.200s).", pw->pw_name,
58- remote_ip);
59+ if (!logged_cert_hostip) {
60+ logit("Authentication tried for %.100s "
61+ "with valid certificate but not "
62+ "from a permitted host "
63+ "(ip=%.200s).", pw->pw_name,
64+ remote_ip);
65+ logged_cert_hostip = 1;
66+ }
67 auth_debug_add("Your address '%.200s' "
68 "is not permitted to use this "
69 "certificate for login.",
70Index: b/auth-options.h
71===================================================================
72--- a/auth-options.h
73+++ b/auth-options.h
74@@ -33,6 +33,7 @@
75 extern int key_is_cert_authority;
76 extern char *authorized_principals;
77
78+void auth_start_parse_options(void);
79 int auth_parse_options(struct passwd *, char *, char *, u_long);
80 void auth_clear_options(void);
81 int auth_cert_options(Key *, struct passwd *);
82Index: b/auth-rsa.c
83===================================================================
84--- a/auth-rsa.c
85+++ b/auth-rsa.c
86@@ -175,6 +175,8 @@
87 if ((f = auth_openkeyfile(file, pw, options.strict_modes)) == NULL)
88 return 0;
89
90+ auth_start_parse_options();
91+
92 /*
93 * Go though the accepted keys, looking for the current key. If
94 * found, perform a challenge-response dialog to verify that the
95Index: b/auth2-pubkey.c
96===================================================================
97--- a/auth2-pubkey.c
98+++ b/auth2-pubkey.c
99@@ -211,6 +211,7 @@
100 restore_uid();
101 return 0;
102 }
103+ auth_start_parse_options();
104 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
105 /* Skip leading whitespace. */
106 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
107@@ -280,6 +281,8 @@
108 found_key = 0;
109 found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
110
111+ auth_start_parse_options();
112+
113 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
114 char *cp, *key_options = NULL;
115
116@@ -416,6 +419,7 @@
117 if (key_cert_check_authority(key, 0, 1,
118 principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
119 goto fail_reason;
120+ auth_start_parse_options();
121 if (auth_cert_options(key, pw) != 0)
122 goto out;
123
diff --git a/debian/patches/authorized-keys-man-symlink.patch b/debian/patches/authorized-keys-man-symlink.patch
new file mode 100644
index 000000000..a9ca85407
--- /dev/null
+++ b/debian/patches/authorized-keys-man-symlink.patch
@@ -0,0 +1,18 @@
1Description: Install authorized_keys(5) as a symlink to sshd(8)
2Author: Tomas Pospisek <tpo_deb@sourcepole.ch>
3Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1720
4Bug-Debian: http://bugs.debian.org/441817
5Last-Update: 2010-03-01
6
7Index: b/Makefile.in
8===================================================================
9--- a/Makefile.in
10+++ b/Makefile.in
11@@ -275,6 +275,7 @@
12 $(INSTALL) -m 644 sshd_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/sshd_config.5
13 $(INSTALL) -m 644 ssh_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh_config.5
14 $(INSTALL) -m 644 sshd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8
15+ ln -s ../$(mansubdir)8/sshd.8 $(DESTDIR)$(mandir)/$(mansubdir)5/authorized_keys.5
16 $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1
17 $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8
18 $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8
diff --git a/debian/patches/debian-banner.patch b/debian/patches/debian-banner.patch
new file mode 100644
index 000000000..57ca35e87
--- /dev/null
+++ b/debian/patches/debian-banner.patch
@@ -0,0 +1,99 @@
1Description: Add DebianBanner server configuration option
2 Setting this to "no" causes sshd to omit the Debian revision from its
3 initial protocol handshake, for those scared by package-versioning.patch.
4Author: Kees Cook <kees@debian.org>
5Bug-Debian: http://bugs.debian.org/562048
6Forwarded: not-needed
7Last-Update: 2010-02-28
8
9Index: b/servconf.c
10===================================================================
11--- a/servconf.c
12+++ b/servconf.c
13@@ -142,6 +142,7 @@
14 options->authorized_principals_file = NULL;
15 options->ip_qos_interactive = -1;
16 options->ip_qos_bulk = -1;
17+ options->debian_banner = -1;
18 }
19
20 void
21@@ -289,6 +290,8 @@
22 options->ip_qos_interactive = IPTOS_LOWDELAY;
23 if (options->ip_qos_bulk == -1)
24 options->ip_qos_bulk = IPTOS_THROUGHPUT;
25+ if (options->debian_banner == -1)
26+ options->debian_banner = 1;
27
28 /* Turn privilege separation on by default */
29 if (use_privsep == -1)
30@@ -338,6 +341,7 @@
31 sZeroKnowledgePasswordAuthentication, sHostCertificate,
32 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
33 sKexAlgorithms, sIPQoS,
34+ sDebianBanner,
35 sDeprecated, sUnsupported
36 } ServerOpCodes;
37
38@@ -473,6 +477,7 @@
39 { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
40 { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
41 { "ipqos", sIPQoS, SSHCFG_ALL },
42+ { "debianbanner", sDebianBanner, SSHCFG_GLOBAL },
43 { NULL, sBadOption, 0 }
44 };
45
46@@ -1436,6 +1441,10 @@
47 }
48 break;
49
50+ case sDebianBanner:
51+ intptr = &options->debian_banner;
52+ goto parse_int;
53+
54 case sDeprecated:
55 logit("%s line %d: Deprecated option %s",
56 filename, linenum, arg);
57Index: b/servconf.h
58===================================================================
59--- a/servconf.h
60+++ b/servconf.h
61@@ -166,6 +166,8 @@
62
63 int num_permitted_opens;
64
65+ int debian_banner;
66+
67 char *chroot_directory;
68 char *revoked_keys_file;
69 char *trusted_user_ca_keys;
70Index: b/sshd.c
71===================================================================
72--- a/sshd.c
73+++ b/sshd.c
74@@ -423,7 +423,8 @@
75 minor = PROTOCOL_MINOR_1;
76 }
77 snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor,
78- SSH_RELEASE, newline);
79+ options.debian_banner ? SSH_RELEASE : SSH_RELEASE_MINIMUM,
80+ newline);
81 server_version_string = xstrdup(buf);
82
83 /* Send our protocol version identification. */
84Index: b/sshd_config.5
85===================================================================
86--- a/sshd_config.5
87+++ b/sshd_config.5
88@@ -340,6 +340,11 @@
89 .Dq no .
90 The default is
91 .Dq delayed .
92+.It Cm DebianBanner
93+Specifies whether the distribution-specified extra version suffix is
94+included during initial protocol handshake.
95+The default is
96+.Dq yes .
97 .It Cm DenyGroups
98 This keyword can be followed by a list of group name patterns, separated
99 by spaces.
diff --git a/debian/patches/debian-config.patch b/debian/patches/debian-config.patch
new file mode 100644
index 000000000..74aa53ecc
--- /dev/null
+++ b/debian/patches/debian-config.patch
@@ -0,0 +1,146 @@
1Description: Various Debian-specific configuration changes
2 ssh: Enable ForwardX11Trusted, returning to earlier semantics which cause
3 fewer problems with existing setups (http://bugs.debian.org/237021).
4 .
5 ssh: Set 'SendEnv LANG LC_*' by default (http://bugs.debian.org/264024).
6 .
7 ssh: Enable HashKnownHosts by default to try to limit the spread of ssh
8 worms.
9 .
10 ssh: Enable GSSAPIAuthentication and disable GSSAPIDelegateCredentials by
11 default.
12 .
13 sshd: Refer to /usr/share/doc/openssh-server/README.Debian.gz alongside
14 PermitRootLogin default.
15 .
16 Document all of this, along with several sshd defaults set in
17 debian/openssh-server.postinst.
18Author: Colin Watson <cjwatson@debian.org>
19Author: Russ Allbery <rra@debian.org>
20Forwarded: not-needed
21Last-Update: 2010-02-28
22
23Index: b/readconf.c
24===================================================================
25--- a/readconf.c
26+++ b/readconf.c
27@@ -1268,7 +1268,7 @@
28 if (options->forward_x11 == -1)
29 options->forward_x11 = 0;
30 if (options->forward_x11_trusted == -1)
31- options->forward_x11_trusted = 0;
32+ options->forward_x11_trusted = 1;
33 if (options->forward_x11_timeout == -1)
34 options->forward_x11_timeout = 1200;
35 if (options->exit_on_forward_failure == -1)
36Index: b/ssh_config
37===================================================================
38--- a/ssh_config
39+++ b/ssh_config
40@@ -17,9 +17,10 @@
41 # list of available options, their meanings and defaults, please see the
42 # ssh_config(5) man page.
43
44-# Host *
45+Host *
46 # ForwardAgent no
47 # ForwardX11 no
48+# ForwardX11Trusted yes
49 # RhostsRSAAuthentication no
50 # RSAAuthentication yes
51 # PasswordAuthentication yes
52@@ -47,3 +48,7 @@
53 # PermitLocalCommand no
54 # VisualHostKey no
55 # ProxyCommand ssh -q -W %h:%p gateway.example.com
56+ SendEnv LANG LC_*
57+ HashKnownHosts yes
58+ GSSAPIAuthentication yes
59+ GSSAPIDelegateCredentials no
60Index: b/ssh_config.5
61===================================================================
62--- a/ssh_config.5
63+++ b/ssh_config.5
64@@ -71,6 +71,22 @@
65 host-specific declarations should be given near the beginning of the
66 file, and general defaults at the end.
67 .Pp
68+Note that the Debian
69+.Ic openssh-client
70+package sets several options as standard in
71+.Pa /etc/ssh/ssh_config
72+which are not the default in
73+.Xr ssh 1 :
74+.Pp
75+.Bl -bullet -offset indent -compact
76+.It
77+.Cm SendEnv No LANG LC_*
78+.It
79+.Cm HashKnownHosts No yes
80+.It
81+.Cm GSSAPIAuthentication No yes
82+.El
83+.Pp
84 The configuration file has the following format:
85 .Pp
86 Empty lines and lines starting with
87@@ -499,7 +515,8 @@
88 Remote clients will be refused access after this time.
89 .Pp
90 The default is
91-.Dq no .
92+.Dq yes
93+(Debian-specific).
94 .Pp
95 See the X11 SECURITY extension specification for full details on
96 the restrictions imposed on untrusted clients.
97Index: b/sshd_config
98===================================================================
99--- a/sshd_config
100+++ b/sshd_config
101@@ -37,6 +37,7 @@
102 # Authentication:
103
104 #LoginGraceTime 2m
105+# See /usr/share/doc/openssh-server/README.Debian.gz.
106 #PermitRootLogin yes
107 #StrictModes yes
108 #MaxAuthTries 6
109Index: b/sshd_config.5
110===================================================================
111--- a/sshd_config.5
112+++ b/sshd_config.5
113@@ -57,6 +57,33 @@
114 .Pq \&"
115 in order to represent arguments containing spaces.
116 .Pp
117+Note that the Debian
118+.Ic openssh-server
119+package sets several options as standard in
120+.Pa /etc/ssh/sshd_config
121+which are not the default in
122+.Xr sshd 8 .
123+The exact list depends on whether the package was installed fresh or
124+upgraded from various possible previous versions, but includes at least the
125+following:
126+.Pp
127+.Bl -bullet -offset indent -compact
128+.It
129+.Cm Protocol No 2
130+.It
131+.Cm ChallengeResponseAuthentication No no
132+.It
133+.Cm X11Forwarding No yes
134+.It
135+.Cm PrintMotd No no
136+.It
137+.Cm AcceptEnv No LANG LC_*
138+.It
139+.Cm Subsystem No sftp /usr/lib/openssh/sftp-server
140+.It
141+.Cm UsePAM No yes
142+.El
143+.Pp
144 The possible
145 keywords and their meanings are as follows (note that
146 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..8e8285a1f
--- /dev/null
+++ b/debian/patches/dnssec-sshfp.patch
@@ -0,0 +1,82 @@
1Description: Force use of DNSSEC even if "options edns0" isn't in resolv.conf
2 This allows SSHFP DNS records to be verified if glibc 2.11 is installed.
3Origin: vendor, https://cvs.fedoraproject.org/viewvc/F-12/openssh/openssh-5.2p1-edns.patch?revision=1.1&view=markup
4Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572049
5Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572049
6Last-Update: 2010-04-06
7
8Index: b/dns.c
9===================================================================
10--- a/dns.c
11+++ b/dns.c
12@@ -177,6 +177,7 @@
13 {
14 u_int counter;
15 int result;
16+ unsigned int rrset_flags = 0;
17 struct rrsetinfo *fingerprints = NULL;
18
19 u_int8_t hostkey_algorithm;
20@@ -200,8 +201,19 @@
21 return -1;
22 }
23
24+ /*
25+ * Original getrrsetbyname function, found on OpenBSD for example,
26+ * doesn't accept any flag and prerequisite for obtaining AD bit in
27+ * DNS response is set by "options edns0" in resolv.conf.
28+ *
29+ * Our version is more clever and use RRSET_FORCE_EDNS0 flag.
30+ */
31+#ifndef HAVE_GETRRSETBYNAME
32+ rrset_flags |= RRSET_FORCE_EDNS0;
33+#endif
34 result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
35- DNS_RDATATYPE_SSHFP, 0, &fingerprints);
36+ DNS_RDATATYPE_SSHFP, rrset_flags, &fingerprints);
37+
38 if (result) {
39 verbose("DNS lookup error: %s", dns_result_totext(result));
40 return -1;
41Index: b/openbsd-compat/getrrsetbyname.c
42===================================================================
43--- a/openbsd-compat/getrrsetbyname.c
44+++ b/openbsd-compat/getrrsetbyname.c
45@@ -209,8 +209,8 @@
46 goto fail;
47 }
48
49- /* don't allow flags yet, unimplemented */
50- if (flags) {
51+ /* Allow RRSET_FORCE_EDNS0 flag only. */
52+ if ((flags & !RRSET_FORCE_EDNS0) != 0) {
53 result = ERRSET_INVAL;
54 goto fail;
55 }
56@@ -226,9 +226,9 @@
57 #endif /* DEBUG */
58
59 #ifdef RES_USE_DNSSEC
60- /* turn on DNSSEC if EDNS0 is configured */
61- if (_resp->options & RES_USE_EDNS0)
62- _resp->options |= RES_USE_DNSSEC;
63+ /* turn on DNSSEC if required */
64+ if (flags & RRSET_FORCE_EDNS0)
65+ _resp->options |= (RES_USE_EDNS0|RES_USE_DNSSEC);
66 #endif /* RES_USE_DNSEC */
67
68 /* make query */
69Index: b/openbsd-compat/getrrsetbyname.h
70===================================================================
71--- a/openbsd-compat/getrrsetbyname.h
72+++ b/openbsd-compat/getrrsetbyname.h
73@@ -72,6 +72,9 @@
74 #ifndef RRSET_VALIDATED
75 # define RRSET_VALIDATED 1
76 #endif
77+#ifndef RRSET_FORCE_EDNS0
78+# define RRSET_FORCE_EDNS0 0x0001
79+#endif
80
81 /*
82 * 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..cec6f6639
--- /dev/null
+++ b/debian/patches/doc-hash-tab-completion.patch
@@ -0,0 +1,20 @@
1Description: Document that HashKnownHosts may break tab-completion
2Author: Colin Watson <cjwatson@debian.org>
3Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1727
4Bug-Debian: http://bugs.debian.org/430154
5Last-Update: 2010-03-01
6
7Index: b/ssh_config.5
8===================================================================
9--- a/ssh_config.5
10+++ b/ssh_config.5
11@@ -585,6 +585,9 @@
12 will not be converted automatically,
13 but may be manually hashed using
14 .Xr ssh-keygen 1 .
15+Use of this option may break facilities such as tab-completion that rely
16+on being able to read unhashed host names from
17+.Pa ~/.ssh/known_hosts .
18 .It Cm HostbasedAuthentication
19 Specifies whether to try rhosts based authentication with public key
20 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..96bbf3a09
--- /dev/null
+++ b/debian/patches/gnome-ssh-askpass2-icon.patch
@@ -0,0 +1,18 @@
1Description: Give the ssh-askpass-gnome window a default icon
2Author: Vincent Untz <vuntz@ubuntu.com>
3Bug-Ubuntu: https://bugs.launchpad.net/bugs/27152
4Last-Update: 2010-02-28
5
6Index: b/contrib/gnome-ssh-askpass2.c
7===================================================================
8--- a/contrib/gnome-ssh-askpass2.c
9+++ b/contrib/gnome-ssh-askpass2.c
10@@ -209,6 +209,8 @@
11
12 gtk_init(&argc, &argv);
13
14+ gtk_window_set_default_icon_from_file ("/usr/share/pixmaps/ssh-askpass-gnome.png", NULL);
15+
16 if (argc > 1) {
17 message = g_strjoinv(" ", argv + 1);
18 } else {
diff --git a/debian/patches/gssapi.patch b/debian/patches/gssapi.patch
new file mode 100644
index 000000000..dc293683e
--- /dev/null
+++ b/debian/patches/gssapi.patch
@@ -0,0 +1,3091 @@
1Description: GSSAPI key exchange support
2 This patch has been rejected upstream: "None of the OpenSSH developers are
3 in favour of adding this, and this situation has not changed for several
4 years. This is not a slight on Simon's patch, which is of fine quality,
5 but just that a) we don't trust GSSAPI implementations that much and b) we
6 don't like adding new KEX since they are pre-auth attack surface. This one
7 is particularly scary, since it requires hooks out to typically root-owned
8 system resources."
9 .
10 However, quite a lot of people rely on this in Debian, and it's better to
11 have it merged into the main openssh package rather than having separate
12 -krb5 packages (as we used to have). It seems to have a generally good
13 security history.
14Author: Simon Wilkinson <simon@sxw.org.uk>
15Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242
16Last-Updated: 2010-02-27
17
18Index: b/ChangeLog.gssapi
19===================================================================
20--- /dev/null
21+++ b/ChangeLog.gssapi
22@@ -0,0 +1,113 @@
23+20110101
24+ - Finally update for OpenSSH 5.6p1
25+ - Add GSSAPIServerIdentity option from Jim Basney
26+
27+20100308
28+ - [ Makefile.in, key.c, key.h ]
29+ Updates for OpenSSH 5.4p1
30+ - [ servconf.c ]
31+ Include GSSAPI options in the sshd -T configuration dump, and flag
32+ some older configuration options as being unsupported. Thanks to Colin
33+ Watson.
34+ -
35+
36+20100124
37+ - [ sshconnect2.c ]
38+ Adapt to deal with additional element in Authmethod structure. Thanks to
39+ Colin Watson
40+
41+20090615
42+ - [ gss-genr.c gss-serv.c kexgssc.c kexgsss.c monitor.c sshconnect2.c
43+ sshd.c ]
44+ Fix issues identified by Greg Hudson following a code review
45+ Check return value of gss_indicate_mechs
46+ Protect GSSAPI calls in monitor, so they can only be used if enabled
47+ Check return values of bignum functions in key exchange
48+ Use BN_clear_free to clear other side's DH value
49+ Make ssh_gssapi_id_kex more robust
50+ Only configure kex table pointers if GSSAPI is enabled
51+ Don't leak mechanism list, or gss mechanism list
52+ Cast data.length before printing
53+ If serverkey isn't provided, use an empty string, rather than NULL
54+
55+20090201
56+ - [ gss-genr.c gss-serv.c kex.h kexgssc.c readconf.c readconf.h ssh-gss.h
57+ ssh_config.5 sshconnet2.c ]
58+ Add support for the GSSAPIClientIdentity option, which allows the user
59+ to specify which GSSAPI identity to use to contact a given server
60+
61+20080404
62+ - [ gss-serv.c ]
63+ Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow
64+ been omitted from a previous version of this patch. Reported by Borislav
65+ Stoichkov
66+
67+20070317
68+ - [ gss-serv-krb5.c ]
69+ Remove C99ism, where new_ccname was being declared in the middle of a
70+ function
71+
72+20061220
73+ - [ servconf.c ]
74+ Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and
75+ documented, behaviour. Reported by Dan Watson.
76+
77+20060910
78+ - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c
79+ ssh-gss.h ]
80+ add support for gss-group14-sha1 key exchange mechanisms
81+ - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ]
82+ Add GSSAPIStrictAcceptorCheck option to allow the disabling of
83+ acceptor principal checking on multi-homed machines.
84+ <Bugzilla #928>
85+ - [ sshd_config ssh_config ]
86+ Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample
87+ configuration files
88+ - [ kexgss.c kegsss.c sshconnect2.c sshd.c ]
89+ Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf()
90+ Limit length of error messages displayed by client
91+
92+20060909
93+ - [ gss-genr.c gss-serv.c ]
94+ move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server
95+ only, where they belong
96+ <Bugzilla #1225>
97+
98+20060829
99+ - [ gss-serv-krb5.c ]
100+ Fix CCAPI credentials cache name when creating KRB5CCNAME environment
101+ variable
102+
103+20060828
104+ - [ gss-genr.c ]
105+ Avoid Heimdal context freeing problem
106+ <Fixed upstream 20060829>
107+
108+20060818
109+ - [ gss-genr.c ssh-gss.h sshconnect2.c ]
110+ Make sure that SPENGO is disabled
111+ <Bugzilla #1218 - Fixed upstream 20060818>
112+
113+20060421
114+ - [ gssgenr.c, sshconnect2.c ]
115+ a few type changes (signed versus unsigned, int versus size_t) to
116+ fix compiler errors/warnings
117+ (from jbasney AT ncsa.uiuc.edu)
118+ - [ kexgssc.c, sshconnect2.c ]
119+ fix uninitialized variable warnings
120+ (from jbasney AT ncsa.uiuc.edu)
121+ - [ gssgenr.c ]
122+ pass oid to gss_display_status (helpful when using GSSAPI mechglue)
123+ (from jbasney AT ncsa.uiuc.edu)
124+ <Bugzilla #1220 >
125+ - [ gss-serv-krb5.c ]
126+ #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H
127+ (from jbasney AT ncsa.uiuc.edu)
128+ <Fixed upstream 20060304>
129+ - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c
130+ add client-side GssapiKeyExchange option
131+ (from jbasney AT ncsa.uiuc.edu)
132+ - [ sshconnect2.c ]
133+ add support for GssapiTrustDns option for gssapi-with-mic
134+ (from jbasney AT ncsa.uiuc.edu)
135+ <gssapi-with-mic support is Bugzilla #1008>
136Index: b/Makefile.in
137===================================================================
138--- a/Makefile.in
139+++ b/Makefile.in
140@@ -70,6 +70,7 @@
141 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
142 monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
143 kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \
144+ kexgssc.o \
145 msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o jpake.o \
146 schnorr.o ssh-pkcs11.o
147
148@@ -86,7 +87,7 @@
149 auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-jpake.o \
150 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \
151 auth-krb5.o \
152- auth2-gss.o gss-serv.o gss-serv-krb5.o \
153+ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o\
154 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
155 sftp-server.o sftp-common.o \
156 roaming_common.o roaming_serv.o \
157Index: b/auth-krb5.c
158===================================================================
159--- a/auth-krb5.c
160+++ b/auth-krb5.c
161@@ -170,8 +170,13 @@
162
163 len = strlen(authctxt->krb5_ticket_file) + 6;
164 authctxt->krb5_ccname = xmalloc(len);
165+#ifdef USE_CCAPI
166+ snprintf(authctxt->krb5_ccname, len, "API:%s",
167+ authctxt->krb5_ticket_file);
168+#else
169 snprintf(authctxt->krb5_ccname, len, "FILE:%s",
170 authctxt->krb5_ticket_file);
171+#endif
172
173 #ifdef USE_PAM
174 if (options.use_pam)
175@@ -226,15 +231,22 @@
176 #ifndef HEIMDAL
177 krb5_error_code
178 ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
179- int tmpfd, ret;
180+ int ret;
181 char ccname[40];
182 mode_t old_umask;
183+#ifdef USE_CCAPI
184+ char cctemplate[] = "API:krb5cc_%d";
185+#else
186+ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
187+ int tmpfd;
188+#endif
189
190 ret = snprintf(ccname, sizeof(ccname),
191- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid());
192+ cctemplate, geteuid());
193 if (ret < 0 || (size_t)ret >= sizeof(ccname))
194 return ENOMEM;
195
196+#ifndef USE_CCAPI
197 old_umask = umask(0177);
198 tmpfd = mkstemp(ccname + strlen("FILE:"));
199 umask(old_umask);
200@@ -249,6 +261,7 @@
201 return errno;
202 }
203 close(tmpfd);
204+#endif
205
206 return (krb5_cc_resolve(ctx, ccname, ccache));
207 }
208Index: b/auth2-gss.c
209===================================================================
210--- a/auth2-gss.c
211+++ b/auth2-gss.c
212@@ -1,7 +1,7 @@
213 /* $OpenBSD: auth2-gss.c,v 1.17 2011/03/10 02:52:57 djm Exp $ */
214
215 /*
216- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
217+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
218 *
219 * Redistribution and use in source and binary forms, with or without
220 * modification, are permitted provided that the following conditions
221@@ -52,6 +52,40 @@
222 static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
223 static void input_gssapi_errtok(int, u_int32_t, void *);
224
225+/*
226+ * The 'gssapi_keyex' userauth mechanism.
227+ */
228+static int
229+userauth_gsskeyex(Authctxt *authctxt)
230+{
231+ int authenticated = 0;
232+ Buffer b;
233+ gss_buffer_desc mic, gssbuf;
234+ u_int len;
235+
236+ mic.value = packet_get_string(&len);
237+ mic.length = len;
238+
239+ packet_check_eom();
240+
241+ ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
242+ "gssapi-keyex");
243+
244+ gssbuf.value = buffer_ptr(&b);
245+ gssbuf.length = buffer_len(&b);
246+
247+ /* gss_kex_context is NULL with privsep, so we can't check it here */
248+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context,
249+ &gssbuf, &mic))))
250+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
251+ authctxt->pw));
252+
253+ buffer_free(&b);
254+ xfree(mic.value);
255+
256+ return (authenticated);
257+}
258+
259 /*
260 * We only support those mechanisms that we know about (ie ones that we know
261 * how to check local user kuserok and the like)
262@@ -244,7 +278,8 @@
263
264 packet_check_eom();
265
266- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
267+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
268+ authctxt->pw));
269
270 authctxt->postponed = 0;
271 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
272@@ -279,7 +314,8 @@
273 gssbuf.length = buffer_len(&b);
274
275 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
276- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
277+ authenticated =
278+ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
279 else
280 logit("GSSAPI MIC check failed");
281
282@@ -294,6 +330,12 @@
283 userauth_finish(authctxt, authenticated, "gssapi-with-mic");
284 }
285
286+Authmethod method_gsskeyex = {
287+ "gssapi-keyex",
288+ userauth_gsskeyex,
289+ &options.gss_authentication
290+};
291+
292 Authmethod method_gssapi = {
293 "gssapi-with-mic",
294 userauth_gssapi,
295Index: b/auth2.c
296===================================================================
297--- a/auth2.c
298+++ b/auth2.c
299@@ -69,6 +69,7 @@
300 extern Authmethod method_kbdint;
301 extern Authmethod method_hostbased;
302 #ifdef GSSAPI
303+extern Authmethod method_gsskeyex;
304 extern Authmethod method_gssapi;
305 #endif
306 #ifdef JPAKE
307@@ -79,6 +80,7 @@
308 &method_none,
309 &method_pubkey,
310 #ifdef GSSAPI
311+ &method_gsskeyex,
312 &method_gssapi,
313 #endif
314 #ifdef JPAKE
315Index: b/clientloop.c
316===================================================================
317--- a/clientloop.c
318+++ b/clientloop.c
319@@ -111,6 +111,10 @@
320 #include "msg.h"
321 #include "roaming.h"
322
323+#ifdef GSSAPI
324+#include "ssh-gss.h"
325+#endif
326+
327 /* import options */
328 extern Options options;
329
330@@ -1508,6 +1512,15 @@
331 /* Do channel operations unless rekeying in progress. */
332 if (!rekeying) {
333 channel_after_select(readset, writeset);
334+
335+#ifdef GSSAPI
336+ if (options.gss_renewal_rekey &&
337+ ssh_gssapi_credentials_updated(GSS_C_NO_CONTEXT)) {
338+ debug("credentials updated - forcing rekey");
339+ need_rekeying = 1;
340+ }
341+#endif
342+
343 if (need_rekeying || packet_need_rekeying()) {
344 debug("need rekeying");
345 xxx_kex->done = 0;
346Index: b/config.h.in
347===================================================================
348--- a/config.h.in
349+++ b/config.h.in
350@@ -1441,6 +1441,9 @@
351 /* Use btmp to log bad logins */
352 #undef USE_BTMP
353
354+/* platform uses an in-memory credentials cache */
355+#undef USE_CCAPI
356+
357 /* Use libedit for sftp */
358 #undef USE_LIBEDIT
359
360@@ -1456,6 +1459,9 @@
361 /* Use PIPES instead of a socketpair() */
362 #undef USE_PIPES
363
364+/* platform has the Security Authorization Session API */
365+#undef USE_SECURITY_SESSION_API
366+
367 /* Define if you have Solaris process contracts */
368 #undef USE_SOLARIS_PROCESS_CONTRACTS
369
370Index: b/configure
371===================================================================
372--- a/configure
373+++ b/configure
374@@ -6521,6 +6521,63 @@
375
376 $as_echo "#define SSH_TUN_PREPEND_AF 1" >>confdefs.h
377
378+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we have the Security Authorization Session API" >&5
379+$as_echo_n "checking if we have the Security Authorization Session API... " >&6; }
380+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
381+/* end confdefs.h. */
382+#include <Security/AuthSession.h>
383+int
384+main ()
385+{
386+SessionCreate(0, 0);
387+ ;
388+ return 0;
389+}
390+_ACEOF
391+if ac_fn_c_try_compile "$LINENO"; then :
392+ ac_cv_use_security_session_api="yes"
393+
394+$as_echo "#define USE_SECURITY_SESSION_API 1" >>confdefs.h
395+
396+ LIBS="$LIBS -framework Security"
397+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
398+$as_echo "yes" >&6; }
399+else
400+ ac_cv_use_security_session_api="no"
401+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
402+$as_echo "no" >&6; }
403+fi
404+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
405+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we have an in-memory credentials cache" >&5
406+$as_echo_n "checking if we have an in-memory credentials cache... " >&6; }
407+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
408+/* end confdefs.h. */
409+#include <Kerberos/Kerberos.h>
410+int
411+main ()
412+{
413+cc_context_t c;
414+ (void) cc_initialize (&c, 0, NULL, NULL);
415+ ;
416+ return 0;
417+}
418+_ACEOF
419+if ac_fn_c_try_compile "$LINENO"; then :
420+
421+$as_echo "#define USE_CCAPI 1" >>confdefs.h
422+
423+ LIBS="$LIBS -framework Security"
424+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
425+$as_echo "yes" >&6; }
426+ if test "x$ac_cv_use_security_session_api" = "xno"; then
427+ as_fn_error $? "*** Need a security framework to use the credentials cache API ***" "$LINENO" 5
428+ fi
429+else
430+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
431+$as_echo "no" >&6; }
432+
433+fi
434+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
435
436 ac_fn_c_check_decl "$LINENO" "AU_IPv4" "ac_cv_have_decl_AU_IPv4" "$ac_includes_default"
437 if test "x$ac_cv_have_decl_AU_IPv4" = xyes; then :
438Index: b/configure.ac
439===================================================================
440--- a/configure.ac
441+++ b/configure.ac
442@@ -515,6 +515,30 @@
443 [Use tunnel device compatibility to OpenBSD])
444 AC_DEFINE([SSH_TUN_PREPEND_AF], [1],
445 [Prepend the address family to IP tunnel traffic])
446+ AC_MSG_CHECKING([if we have the Security Authorization Session API])
447+ AC_TRY_COMPILE([#include <Security/AuthSession.h>],
448+ [SessionCreate(0, 0);],
449+ [ac_cv_use_security_session_api="yes"
450+ AC_DEFINE([USE_SECURITY_SESSION_API], [1],
451+ [platform has the Security Authorization Session API])
452+ LIBS="$LIBS -framework Security"
453+ AC_MSG_RESULT([yes])],
454+ [ac_cv_use_security_session_api="no"
455+ AC_MSG_RESULT([no])])
456+ AC_MSG_CHECKING([if we have an in-memory credentials cache])
457+ AC_TRY_COMPILE(
458+ [#include <Kerberos/Kerberos.h>],
459+ [cc_context_t c;
460+ (void) cc_initialize (&c, 0, NULL, NULL);],
461+ [AC_DEFINE([USE_CCAPI], [1],
462+ [platform uses an in-memory credentials cache])
463+ LIBS="$LIBS -framework Security"
464+ AC_MSG_RESULT([yes])
465+ if test "x$ac_cv_use_security_session_api" = "xno"; then
466+ AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***])
467+ fi],
468+ [AC_MSG_RESULT([no])]
469+ )
470 m4_pattern_allow([AU_IPv])
471 AC_CHECK_DECL([AU_IPv4], [],
472 AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records])
473Index: b/gss-genr.c
474===================================================================
475--- a/gss-genr.c
476+++ b/gss-genr.c
477@@ -1,7 +1,7 @@
478 /* $OpenBSD: gss-genr.c,v 1.20 2009/06/22 05:39:28 dtucker Exp $ */
479
480 /*
481- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
482+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
483 *
484 * Redistribution and use in source and binary forms, with or without
485 * modification, are permitted provided that the following conditions
486@@ -39,12 +39,167 @@
487 #include "buffer.h"
488 #include "log.h"
489 #include "ssh2.h"
490+#include "cipher.h"
491+#include "key.h"
492+#include "kex.h"
493+#include <openssl/evp.h>
494
495 #include "ssh-gss.h"
496
497 extern u_char *session_id2;
498 extern u_int session_id2_len;
499
500+typedef struct {
501+ char *encoded;
502+ gss_OID oid;
503+} ssh_gss_kex_mapping;
504+
505+/*
506+ * XXX - It would be nice to find a more elegant way of handling the
507+ * XXX passing of the key exchange context to the userauth routines
508+ */
509+
510+Gssctxt *gss_kex_context = NULL;
511+
512+static ssh_gss_kex_mapping *gss_enc2oid = NULL;
513+
514+int
515+ssh_gssapi_oid_table_ok() {
516+ return (gss_enc2oid != NULL);
517+}
518+
519+/*
520+ * Return a list of the gss-group1-sha1 mechanisms supported by this program
521+ *
522+ * We test mechanisms to ensure that we can use them, to avoid starting
523+ * a key exchange with a bad mechanism
524+ */
525+
526+char *
527+ssh_gssapi_client_mechanisms(const char *host, const char *client) {
528+ gss_OID_set gss_supported;
529+ OM_uint32 min_status;
530+
531+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported)))
532+ return NULL;
533+
534+ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism,
535+ host, client));
536+}
537+
538+char *
539+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
540+ const char *host, const char *client) {
541+ Buffer buf;
542+ size_t i;
543+ int oidpos, enclen;
544+ char *mechs, *encoded;
545+ u_char digest[EVP_MAX_MD_SIZE];
546+ char deroid[2];
547+ const EVP_MD *evp_md = EVP_md5();
548+ EVP_MD_CTX md;
549+
550+ if (gss_enc2oid != NULL) {
551+ for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
552+ xfree(gss_enc2oid[i].encoded);
553+ xfree(gss_enc2oid);
554+ }
555+
556+ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) *
557+ (gss_supported->count + 1));
558+
559+ buffer_init(&buf);
560+
561+ oidpos = 0;
562+ for (i = 0; i < gss_supported->count; i++) {
563+ if (gss_supported->elements[i].length < 128 &&
564+ (*check)(NULL, &(gss_supported->elements[i]), host, client)) {
565+
566+ deroid[0] = SSH_GSS_OIDTYPE;
567+ deroid[1] = gss_supported->elements[i].length;
568+
569+ EVP_DigestInit(&md, evp_md);
570+ EVP_DigestUpdate(&md, deroid, 2);
571+ EVP_DigestUpdate(&md,
572+ gss_supported->elements[i].elements,
573+ gss_supported->elements[i].length);
574+ EVP_DigestFinal(&md, digest, NULL);
575+
576+ encoded = xmalloc(EVP_MD_size(evp_md) * 2);
577+ enclen = __b64_ntop(digest, EVP_MD_size(evp_md),
578+ encoded, EVP_MD_size(evp_md) * 2);
579+
580+ if (oidpos != 0)
581+ buffer_put_char(&buf, ',');
582+
583+ buffer_append(&buf, KEX_GSS_GEX_SHA1_ID,
584+ sizeof(KEX_GSS_GEX_SHA1_ID) - 1);
585+ buffer_append(&buf, encoded, enclen);
586+ buffer_put_char(&buf, ',');
587+ buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID,
588+ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1);
589+ buffer_append(&buf, encoded, enclen);
590+ buffer_put_char(&buf, ',');
591+ buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID,
592+ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1);
593+ buffer_append(&buf, encoded, enclen);
594+
595+ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
596+ gss_enc2oid[oidpos].encoded = encoded;
597+ oidpos++;
598+ }
599+ }
600+ gss_enc2oid[oidpos].oid = NULL;
601+ gss_enc2oid[oidpos].encoded = NULL;
602+
603+ buffer_put_char(&buf, '\0');
604+
605+ mechs = xmalloc(buffer_len(&buf));
606+ buffer_get(&buf, mechs, buffer_len(&buf));
607+ buffer_free(&buf);
608+
609+ if (strlen(mechs) == 0) {
610+ xfree(mechs);
611+ mechs = NULL;
612+ }
613+
614+ return (mechs);
615+}
616+
617+gss_OID
618+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) {
619+ int i = 0;
620+
621+ switch (kex_type) {
622+ case KEX_GSS_GRP1_SHA1:
623+ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID))
624+ return GSS_C_NO_OID;
625+ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1;
626+ break;
627+ case KEX_GSS_GRP14_SHA1:
628+ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID))
629+ return GSS_C_NO_OID;
630+ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1;
631+ break;
632+ case KEX_GSS_GEX_SHA1:
633+ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID))
634+ return GSS_C_NO_OID;
635+ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1;
636+ break;
637+ default:
638+ return GSS_C_NO_OID;
639+ }
640+
641+ while (gss_enc2oid[i].encoded != NULL &&
642+ strcmp(name, gss_enc2oid[i].encoded) != 0)
643+ i++;
644+
645+ if (gss_enc2oid[i].oid != NULL && ctx != NULL)
646+ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
647+
648+ return gss_enc2oid[i].oid;
649+}
650+
651 /* Check that the OID in a data stream matches that in the context */
652 int
653 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
654@@ -197,7 +352,7 @@
655 }
656
657 ctx->major = gss_init_sec_context(&ctx->minor,
658- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid,
659+ ctx->client_creds, &ctx->context, ctx->name, ctx->oid,
660 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag,
661 0, NULL, recv_tok, NULL, send_tok, flags, NULL);
662
663@@ -227,8 +382,42 @@
664 }
665
666 OM_uint32
667+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name)
668+{
669+ gss_buffer_desc gssbuf;
670+ gss_name_t gssname;
671+ OM_uint32 status;
672+ gss_OID_set oidset;
673+
674+ gssbuf.value = (void *) name;
675+ gssbuf.length = strlen(gssbuf.value);
676+
677+ gss_create_empty_oid_set(&status, &oidset);
678+ gss_add_oid_set_member(&status, ctx->oid, &oidset);
679+
680+ ctx->major = gss_import_name(&ctx->minor, &gssbuf,
681+ GSS_C_NT_USER_NAME, &gssname);
682+
683+ if (!ctx->major)
684+ ctx->major = gss_acquire_cred(&ctx->minor,
685+ gssname, 0, oidset, GSS_C_INITIATE,
686+ &ctx->client_creds, NULL, NULL);
687+
688+ gss_release_name(&status, &gssname);
689+ gss_release_oid_set(&status, &oidset);
690+
691+ if (ctx->major)
692+ ssh_gssapi_error(ctx);
693+
694+ return(ctx->major);
695+}
696+
697+OM_uint32
698 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
699 {
700+ if (ctx == NULL)
701+ return -1;
702+
703 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context,
704 GSS_C_QOP_DEFAULT, buffer, hash)))
705 ssh_gssapi_error(ctx);
706@@ -236,6 +425,19 @@
707 return (ctx->major);
708 }
709
710+/* Priviledged when used by server */
711+OM_uint32
712+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
713+{
714+ if (ctx == NULL)
715+ return -1;
716+
717+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
718+ gssbuf, gssmic, NULL);
719+
720+ return (ctx->major);
721+}
722+
723 void
724 ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service,
725 const char *context)
726@@ -249,11 +451,16 @@
727 }
728
729 int
730-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
731+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host,
732+ const char *client)
733 {
734 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
735 OM_uint32 major, minor;
736 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
737+ Gssctxt *intctx = NULL;
738+
739+ if (ctx == NULL)
740+ ctx = &intctx;
741
742 /* RFC 4462 says we MUST NOT do SPNEGO */
743 if (oid->length == spnego_oid.length &&
744@@ -263,6 +470,10 @@
745 ssh_gssapi_build_ctx(ctx);
746 ssh_gssapi_set_oid(*ctx, oid);
747 major = ssh_gssapi_import_name(*ctx, host);
748+
749+ if (!GSS_ERROR(major) && client)
750+ major = ssh_gssapi_client_identity(*ctx, client);
751+
752 if (!GSS_ERROR(major)) {
753 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token,
754 NULL);
755@@ -272,10 +483,67 @@
756 GSS_C_NO_BUFFER);
757 }
758
759- if (GSS_ERROR(major))
760+ if (GSS_ERROR(major) || intctx != NULL)
761 ssh_gssapi_delete_ctx(ctx);
762
763 return (!GSS_ERROR(major));
764 }
765
766+int
767+ssh_gssapi_credentials_updated(Gssctxt *ctxt) {
768+ static gss_name_t saved_name = GSS_C_NO_NAME;
769+ static OM_uint32 saved_lifetime = 0;
770+ static gss_OID saved_mech = GSS_C_NO_OID;
771+ static gss_name_t name;
772+ static OM_uint32 last_call = 0;
773+ OM_uint32 lifetime, now, major, minor;
774+ int equal;
775+ gss_cred_usage_t usage = GSS_C_INITIATE;
776+
777+ now = time(NULL);
778+
779+ if (ctxt) {
780+ debug("Rekey has happened - updating saved versions");
781+
782+ if (saved_name != GSS_C_NO_NAME)
783+ gss_release_name(&minor, &saved_name);
784+
785+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
786+ &saved_name, &saved_lifetime, NULL, NULL);
787+
788+ if (!GSS_ERROR(major)) {
789+ saved_mech = ctxt->oid;
790+ saved_lifetime+= now;
791+ } else {
792+ /* Handle the error */
793+ }
794+ return 0;
795+ }
796+
797+ if (now - last_call < 10)
798+ return 0;
799+
800+ last_call = now;
801+
802+ if (saved_mech == GSS_C_NO_OID)
803+ return 0;
804+
805+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL,
806+ &name, &lifetime, NULL, NULL);
807+ if (major == GSS_S_CREDENTIALS_EXPIRED)
808+ return 0;
809+ else if (GSS_ERROR(major))
810+ return 0;
811+
812+ major = gss_compare_name(&minor, saved_name, name, &equal);
813+ gss_release_name(&minor, &name);
814+ if (GSS_ERROR(major))
815+ return 0;
816+
817+ if (equal && (saved_lifetime < lifetime + now - 10))
818+ return 1;
819+
820+ return 0;
821+}
822+
823 #endif /* GSSAPI */
824Index: b/gss-serv-krb5.c
825===================================================================
826--- a/gss-serv-krb5.c
827+++ b/gss-serv-krb5.c
828@@ -1,7 +1,7 @@
829 /* $OpenBSD: gss-serv-krb5.c,v 1.7 2006/08/03 03:34:42 deraadt Exp $ */
830
831 /*
832- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
833+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
834 *
835 * Redistribution and use in source and binary forms, with or without
836 * modification, are permitted provided that the following conditions
837@@ -120,6 +120,7 @@
838 krb5_principal princ;
839 OM_uint32 maj_status, min_status;
840 int len;
841+ const char *new_ccname;
842
843 if (client->creds == NULL) {
844 debug("No credentials stored");
845@@ -168,11 +169,16 @@
846 return;
847 }
848
849- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
850+ new_ccname = krb5_cc_get_name(krb_context, ccache);
851+
852 client->store.envvar = "KRB5CCNAME";
853- len = strlen(client->store.filename) + 6;
854- client->store.envval = xmalloc(len);
855- snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
856+#ifdef USE_CCAPI
857+ xasprintf(&client->store.envval, "API:%s", new_ccname);
858+ client->store.filename = NULL;
859+#else
860+ xasprintf(&client->store.envval, "FILE:%s", new_ccname);
861+ client->store.filename = xstrdup(new_ccname);
862+#endif
863
864 #ifdef USE_PAM
865 if (options.use_pam)
866@@ -184,6 +190,71 @@
867 return;
868 }
869
870+int
871+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
872+ ssh_gssapi_client *client)
873+{
874+ krb5_ccache ccache = NULL;
875+ krb5_principal principal = NULL;
876+ char *name = NULL;
877+ krb5_error_code problem;
878+ OM_uint32 maj_status, min_status;
879+
880+ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
881+ logit("krb5_cc_resolve(): %.100s",
882+ krb5_get_err_text(krb_context, problem));
883+ return 0;
884+ }
885+
886+ /* Find out who the principal in this cache is */
887+ if ((problem = krb5_cc_get_principal(krb_context, ccache,
888+ &principal))) {
889+ logit("krb5_cc_get_principal(): %.100s",
890+ krb5_get_err_text(krb_context, problem));
891+ krb5_cc_close(krb_context, ccache);
892+ return 0;
893+ }
894+
895+ if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
896+ logit("krb5_unparse_name(): %.100s",
897+ krb5_get_err_text(krb_context, problem));
898+ krb5_free_principal(krb_context, principal);
899+ krb5_cc_close(krb_context, ccache);
900+ return 0;
901+ }
902+
903+
904+ if (strcmp(name,client->exportedname.value)!=0) {
905+ debug("Name in local credentials cache differs. Not storing");
906+ krb5_free_principal(krb_context, principal);
907+ krb5_cc_close(krb_context, ccache);
908+ krb5_free_unparsed_name(krb_context, name);
909+ return 0;
910+ }
911+ krb5_free_unparsed_name(krb_context, name);
912+
913+ /* Name matches, so lets get on with it! */
914+
915+ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
916+ logit("krb5_cc_initialize(): %.100s",
917+ krb5_get_err_text(krb_context, problem));
918+ krb5_free_principal(krb_context, principal);
919+ krb5_cc_close(krb_context, ccache);
920+ return 0;
921+ }
922+
923+ krb5_free_principal(krb_context, principal);
924+
925+ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
926+ ccache))) {
927+ logit("gss_krb5_copy_ccache() failed. Sorry!");
928+ krb5_cc_close(krb_context, ccache);
929+ return 0;
930+ }
931+
932+ return 1;
933+}
934+
935 ssh_gssapi_mech gssapi_kerberos_mech = {
936 "toWM5Slw5Ew8Mqkay+al2g==",
937 "Kerberos",
938@@ -191,7 +262,8 @@
939 NULL,
940 &ssh_gssapi_krb5_userok,
941 NULL,
942- &ssh_gssapi_krb5_storecreds
943+ &ssh_gssapi_krb5_storecreds,
944+ &ssh_gssapi_krb5_updatecreds
945 };
946
947 #endif /* KRB5 */
948Index: b/gss-serv.c
949===================================================================
950--- a/gss-serv.c
951+++ b/gss-serv.c
952@@ -1,7 +1,7 @@
953 /* $OpenBSD: gss-serv.c,v 1.23 2011/08/01 19:18:15 markus Exp $ */
954
955 /*
956- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
957+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
958 *
959 * Redistribution and use in source and binary forms, with or without
960 * modification, are permitted provided that the following conditions
961@@ -45,15 +45,20 @@
962 #include "channels.h"
963 #include "session.h"
964 #include "misc.h"
965+#include "servconf.h"
966+#include "uidswap.h"
967
968 #include "ssh-gss.h"
969+#include "monitor_wrap.h"
970+
971+extern ServerOptions options;
972
973 static ssh_gssapi_client gssapi_client =
974 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
975- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL}};
976+ GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL}, 0, 0};
977
978 ssh_gssapi_mech gssapi_null_mech =
979- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
980+ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
981
982 #ifdef KRB5
983 extern ssh_gssapi_mech gssapi_kerberos_mech;
984@@ -81,25 +86,32 @@
985 char lname[MAXHOSTNAMELEN];
986 gss_OID_set oidset;
987
988- gss_create_empty_oid_set(&status, &oidset);
989- gss_add_oid_set_member(&status, ctx->oid, &oidset);
990+ if (options.gss_strict_acceptor) {
991+ gss_create_empty_oid_set(&status, &oidset);
992+ gss_add_oid_set_member(&status, ctx->oid, &oidset);
993+
994+ if (gethostname(lname, MAXHOSTNAMELEN)) {
995+ gss_release_oid_set(&status, &oidset);
996+ return (-1);
997+ }
998
999- if (gethostname(lname, MAXHOSTNAMELEN)) {
1000- gss_release_oid_set(&status, &oidset);
1001- return (-1);
1002- }
1003+ if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
1004+ gss_release_oid_set(&status, &oidset);
1005+ return (ctx->major);
1006+ }
1007+
1008+ if ((ctx->major = gss_acquire_cred(&ctx->minor,
1009+ ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds,
1010+ NULL, NULL)))
1011+ ssh_gssapi_error(ctx);
1012
1013- if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
1014 gss_release_oid_set(&status, &oidset);
1015 return (ctx->major);
1016+ } else {
1017+ ctx->name = GSS_C_NO_NAME;
1018+ ctx->creds = GSS_C_NO_CREDENTIAL;
1019 }
1020-
1021- if ((ctx->major = gss_acquire_cred(&ctx->minor,
1022- ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL)))
1023- ssh_gssapi_error(ctx);
1024-
1025- gss_release_oid_set(&status, &oidset);
1026- return (ctx->major);
1027+ return GSS_S_COMPLETE;
1028 }
1029
1030 /* Privileged */
1031@@ -114,6 +126,29 @@
1032 }
1033
1034 /* Unprivileged */
1035+char *
1036+ssh_gssapi_server_mechanisms() {
1037+ gss_OID_set supported;
1038+
1039+ ssh_gssapi_supported_oids(&supported);
1040+ return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
1041+ NULL, NULL));
1042+}
1043+
1044+/* Unprivileged */
1045+int
1046+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
1047+ const char *dummy) {
1048+ Gssctxt *ctx = NULL;
1049+ int res;
1050+
1051+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
1052+ ssh_gssapi_delete_ctx(&ctx);
1053+
1054+ return (res);
1055+}
1056+
1057+/* Unprivileged */
1058 void
1059 ssh_gssapi_supported_oids(gss_OID_set *oidset)
1060 {
1061@@ -123,7 +158,9 @@
1062 gss_OID_set supported;
1063
1064 gss_create_empty_oid_set(&min_status, oidset);
1065- gss_indicate_mechs(&min_status, &supported);
1066+
1067+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
1068+ return;
1069
1070 while (supported_mechs[i]->name != NULL) {
1071 if (GSS_ERROR(gss_test_oid_set_member(&min_status,
1072@@ -249,8 +286,48 @@
1073 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
1074 {
1075 int i = 0;
1076+ int equal = 0;
1077+ gss_name_t new_name = GSS_C_NO_NAME;
1078+ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
1079+
1080+ if (options.gss_store_rekey && client->used && ctx->client_creds) {
1081+ if (client->mech->oid.length != ctx->oid->length ||
1082+ (memcmp(client->mech->oid.elements,
1083+ ctx->oid->elements, ctx->oid->length) !=0)) {
1084+ debug("Rekeyed credentials have different mechanism");
1085+ return GSS_S_COMPLETE;
1086+ }
1087+
1088+ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
1089+ ctx->client_creds, ctx->oid, &new_name,
1090+ NULL, NULL, NULL))) {
1091+ ssh_gssapi_error(ctx);
1092+ return (ctx->major);
1093+ }
1094+
1095+ ctx->major = gss_compare_name(&ctx->minor, client->name,
1096+ new_name, &equal);
1097
1098- gss_buffer_desc ename;
1099+ if (GSS_ERROR(ctx->major)) {
1100+ ssh_gssapi_error(ctx);
1101+ return (ctx->major);
1102+ }
1103+
1104+ if (!equal) {
1105+ debug("Rekeyed credentials have different name");
1106+ return GSS_S_COMPLETE;
1107+ }
1108+
1109+ debug("Marking rekeyed credentials for export");
1110+
1111+ gss_release_name(&ctx->minor, &client->name);
1112+ gss_release_cred(&ctx->minor, &client->creds);
1113+ client->name = new_name;
1114+ client->creds = ctx->client_creds;
1115+ ctx->client_creds = GSS_C_NO_CREDENTIAL;
1116+ client->updated = 1;
1117+ return GSS_S_COMPLETE;
1118+ }
1119
1120 client->mech = NULL;
1121
1122@@ -265,6 +342,13 @@
1123 if (client->mech == NULL)
1124 return GSS_S_FAILURE;
1125
1126+ if (ctx->client_creds &&
1127+ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
1128+ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
1129+ ssh_gssapi_error(ctx);
1130+ return (ctx->major);
1131+ }
1132+
1133 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
1134 &client->displayname, NULL))) {
1135 ssh_gssapi_error(ctx);
1136@@ -282,6 +366,8 @@
1137 return (ctx->major);
1138 }
1139
1140+ gss_release_buffer(&ctx->minor, &ename);
1141+
1142 /* We can't copy this structure, so we just move the pointer to it */
1143 client->creds = ctx->client_creds;
1144 ctx->client_creds = GSS_C_NO_CREDENTIAL;
1145@@ -329,7 +415,7 @@
1146
1147 /* Privileged */
1148 int
1149-ssh_gssapi_userok(char *user)
1150+ssh_gssapi_userok(char *user, struct passwd *pw)
1151 {
1152 OM_uint32 lmin;
1153
1154@@ -339,9 +425,11 @@
1155 return 0;
1156 }
1157 if (gssapi_client.mech && gssapi_client.mech->userok)
1158- if ((*gssapi_client.mech->userok)(&gssapi_client, user))
1159+ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
1160+ gssapi_client.used = 1;
1161+ gssapi_client.store.owner = pw;
1162 return 1;
1163- else {
1164+ } else {
1165 /* Destroy delegated credentials if userok fails */
1166 gss_release_buffer(&lmin, &gssapi_client.displayname);
1167 gss_release_buffer(&lmin, &gssapi_client.exportedname);
1168@@ -354,14 +442,90 @@
1169 return (0);
1170 }
1171
1172-/* Privileged */
1173-OM_uint32
1174-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
1175+/* These bits are only used for rekeying. The unpriviledged child is running
1176+ * as the user, the monitor is root.
1177+ *
1178+ * In the child, we want to :
1179+ * *) Ask the monitor to store our credentials into the store we specify
1180+ * *) If it succeeds, maybe do a PAM update
1181+ */
1182+
1183+/* Stuff for PAM */
1184+
1185+#ifdef USE_PAM
1186+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
1187+ struct pam_response **resp, void *data)
1188 {
1189- ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
1190- gssbuf, gssmic, NULL);
1191+ return (PAM_CONV_ERR);
1192+}
1193+#endif
1194
1195- return (ctx->major);
1196+void
1197+ssh_gssapi_rekey_creds() {
1198+ int ok;
1199+ int ret;
1200+#ifdef USE_PAM
1201+ pam_handle_t *pamh = NULL;
1202+ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
1203+ char *envstr;
1204+#endif
1205+
1206+ if (gssapi_client.store.filename == NULL &&
1207+ gssapi_client.store.envval == NULL &&
1208+ gssapi_client.store.envvar == NULL)
1209+ return;
1210+
1211+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
1212+
1213+ if (!ok)
1214+ return;
1215+
1216+ debug("Rekeyed credentials stored successfully");
1217+
1218+ /* Actually managing to play with the ssh pam stack from here will
1219+ * be next to impossible. In any case, we may want different options
1220+ * for rekeying. So, use our own :)
1221+ */
1222+#ifdef USE_PAM
1223+ if (!use_privsep) {
1224+ debug("Not even going to try and do PAM with privsep disabled");
1225+ return;
1226+ }
1227+
1228+ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
1229+ &pamconv, &pamh);
1230+ if (ret)
1231+ return;
1232+
1233+ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
1234+ gssapi_client.store.envval);
1235+
1236+ ret = pam_putenv(pamh, envstr);
1237+ if (!ret)
1238+ pam_setcred(pamh, PAM_REINITIALIZE_CRED);
1239+ pam_end(pamh, PAM_SUCCESS);
1240+#endif
1241+}
1242+
1243+int
1244+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
1245+ int ok = 0;
1246+
1247+ /* Check we've got credentials to store */
1248+ if (!gssapi_client.updated)
1249+ return 0;
1250+
1251+ gssapi_client.updated = 0;
1252+
1253+ temporarily_use_uid(gssapi_client.store.owner);
1254+ if (gssapi_client.mech && gssapi_client.mech->updatecreds)
1255+ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
1256+ else
1257+ debug("No update function for this mechanism");
1258+
1259+ restore_uid();
1260+
1261+ return ok;
1262 }
1263
1264 #endif
1265Index: b/kex.c
1266===================================================================
1267--- a/kex.c
1268+++ b/kex.c
1269@@ -50,6 +50,10 @@
1270 #include "monitor.h"
1271 #include "roaming.h"
1272
1273+#ifdef GSSAPI
1274+#include "ssh-gss.h"
1275+#endif
1276+
1277 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
1278 # if defined(HAVE_EVP_SHA256)
1279 # define evp_ssh_sha256 EVP_sha256
1280@@ -358,6 +362,20 @@
1281 k->kex_type = KEX_ECDH_SHA2;
1282 k->evp_md = kex_ecdh_name_to_evpmd(k->name);
1283 #endif
1284+#ifdef GSSAPI
1285+ } else if (strncmp(k->name, KEX_GSS_GEX_SHA1_ID,
1286+ sizeof(KEX_GSS_GEX_SHA1_ID) - 1) == 0) {
1287+ k->kex_type = KEX_GSS_GEX_SHA1;
1288+ k->evp_md = EVP_sha1();
1289+ } else if (strncmp(k->name, KEX_GSS_GRP1_SHA1_ID,
1290+ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1) == 0) {
1291+ k->kex_type = KEX_GSS_GRP1_SHA1;
1292+ k->evp_md = EVP_sha1();
1293+ } else if (strncmp(k->name, KEX_GSS_GRP14_SHA1_ID,
1294+ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1) == 0) {
1295+ k->kex_type = KEX_GSS_GRP14_SHA1;
1296+ k->evp_md = EVP_sha1();
1297+#endif
1298 } else
1299 fatal("bad kex alg %s", k->name);
1300 }
1301Index: b/kex.h
1302===================================================================
1303--- a/kex.h
1304+++ b/kex.h
1305@@ -73,6 +73,9 @@
1306 KEX_DH_GEX_SHA1,
1307 KEX_DH_GEX_SHA256,
1308 KEX_ECDH_SHA2,
1309+ KEX_GSS_GRP1_SHA1,
1310+ KEX_GSS_GRP14_SHA1,
1311+ KEX_GSS_GEX_SHA1,
1312 KEX_MAX
1313 };
1314
1315@@ -129,6 +132,12 @@
1316 sig_atomic_t done;
1317 int flags;
1318 const EVP_MD *evp_md;
1319+#ifdef GSSAPI
1320+ int gss_deleg_creds;
1321+ int gss_trust_dns;
1322+ char *gss_host;
1323+ char *gss_client;
1324+#endif
1325 char *client_version_string;
1326 char *server_version_string;
1327 int (*verify_host_key)(Key *);
1328@@ -156,6 +165,11 @@
1329 void kexecdh_client(Kex *);
1330 void kexecdh_server(Kex *);
1331
1332+#ifdef GSSAPI
1333+void kexgss_client(Kex *);
1334+void kexgss_server(Kex *);
1335+#endif
1336+
1337 void
1338 kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int,
1339 BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *);
1340Index: b/kexgssc.c
1341===================================================================
1342--- /dev/null
1343+++ b/kexgssc.c
1344@@ -0,0 +1,334 @@
1345+/*
1346+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1347+ *
1348+ * Redistribution and use in source and binary forms, with or without
1349+ * modification, are permitted provided that the following conditions
1350+ * are met:
1351+ * 1. Redistributions of source code must retain the above copyright
1352+ * notice, this list of conditions and the following disclaimer.
1353+ * 2. Redistributions in binary form must reproduce the above copyright
1354+ * notice, this list of conditions and the following disclaimer in the
1355+ * documentation and/or other materials provided with the distribution.
1356+ *
1357+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1358+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1359+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1360+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1361+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1362+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1363+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1364+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1365+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1366+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1367+ */
1368+
1369+#include "includes.h"
1370+
1371+#ifdef GSSAPI
1372+
1373+#include "includes.h"
1374+
1375+#include <openssl/crypto.h>
1376+#include <openssl/bn.h>
1377+
1378+#include <string.h>
1379+
1380+#include "xmalloc.h"
1381+#include "buffer.h"
1382+#include "ssh2.h"
1383+#include "key.h"
1384+#include "cipher.h"
1385+#include "kex.h"
1386+#include "log.h"
1387+#include "packet.h"
1388+#include "dh.h"
1389+
1390+#include "ssh-gss.h"
1391+
1392+void
1393+kexgss_client(Kex *kex) {
1394+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
1395+ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
1396+ Gssctxt *ctxt;
1397+ OM_uint32 maj_status, min_status, ret_flags;
1398+ u_int klen, kout, slen = 0, hashlen, strlen;
1399+ DH *dh;
1400+ BIGNUM *dh_server_pub = NULL;
1401+ BIGNUM *shared_secret = NULL;
1402+ BIGNUM *p = NULL;
1403+ BIGNUM *g = NULL;
1404+ u_char *kbuf, *hash;
1405+ u_char *serverhostkey = NULL;
1406+ u_char *empty = "";
1407+ char *msg;
1408+ char *lang;
1409+ int type = 0;
1410+ int first = 1;
1411+ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
1412+
1413+ /* Initialise our GSSAPI world */
1414+ ssh_gssapi_build_ctx(&ctxt);
1415+ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
1416+ == GSS_C_NO_OID)
1417+ fatal("Couldn't identify host exchange");
1418+
1419+ if (ssh_gssapi_import_name(ctxt, kex->gss_host))
1420+ fatal("Couldn't import hostname");
1421+
1422+ if (kex->gss_client &&
1423+ ssh_gssapi_client_identity(ctxt, kex->gss_client))
1424+ fatal("Couldn't acquire client credentials");
1425+
1426+ switch (kex->kex_type) {
1427+ case KEX_GSS_GRP1_SHA1:
1428+ dh = dh_new_group1();
1429+ break;
1430+ case KEX_GSS_GRP14_SHA1:
1431+ dh = dh_new_group14();
1432+ break;
1433+ case KEX_GSS_GEX_SHA1:
1434+ debug("Doing group exchange\n");
1435+ nbits = dh_estimate(kex->we_need * 8);
1436+ packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
1437+ packet_put_int(min);
1438+ packet_put_int(nbits);
1439+ packet_put_int(max);
1440+
1441+ packet_send();
1442+
1443+ packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
1444+
1445+ if ((p = BN_new()) == NULL)
1446+ fatal("BN_new() failed");
1447+ packet_get_bignum2(p);
1448+ if ((g = BN_new()) == NULL)
1449+ fatal("BN_new() failed");
1450+ packet_get_bignum2(g);
1451+ packet_check_eom();
1452+
1453+ if (BN_num_bits(p) < min || BN_num_bits(p) > max)
1454+ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
1455+ min, BN_num_bits(p), max);
1456+
1457+ dh = dh_new_group(g, p);
1458+ break;
1459+ default:
1460+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1461+ }
1462+
1463+ /* Step 1 - e is dh->pub_key */
1464+ dh_gen_key(dh, kex->we_need * 8);
1465+
1466+ /* This is f, we initialise it now to make life easier */
1467+ dh_server_pub = BN_new();
1468+ if (dh_server_pub == NULL)
1469+ fatal("dh_server_pub == NULL");
1470+
1471+ token_ptr = GSS_C_NO_BUFFER;
1472+
1473+ do {
1474+ debug("Calling gss_init_sec_context");
1475+
1476+ maj_status = ssh_gssapi_init_ctx(ctxt,
1477+ kex->gss_deleg_creds, token_ptr, &send_tok,
1478+ &ret_flags);
1479+
1480+ if (GSS_ERROR(maj_status)) {
1481+ if (send_tok.length != 0) {
1482+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1483+ packet_put_string(send_tok.value,
1484+ send_tok.length);
1485+ }
1486+ fatal("gss_init_context failed");
1487+ }
1488+
1489+ /* If we've got an old receive buffer get rid of it */
1490+ if (token_ptr != GSS_C_NO_BUFFER)
1491+ xfree(recv_tok.value);
1492+
1493+ if (maj_status == GSS_S_COMPLETE) {
1494+ /* If mutual state flag is not true, kex fails */
1495+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
1496+ fatal("Mutual authentication failed");
1497+
1498+ /* If integ avail flag is not true kex fails */
1499+ if (!(ret_flags & GSS_C_INTEG_FLAG))
1500+ fatal("Integrity check failed");
1501+ }
1502+
1503+ /*
1504+ * If we have data to send, then the last message that we
1505+ * received cannot have been a 'complete'.
1506+ */
1507+ if (send_tok.length != 0) {
1508+ if (first) {
1509+ packet_start(SSH2_MSG_KEXGSS_INIT);
1510+ packet_put_string(send_tok.value,
1511+ send_tok.length);
1512+ packet_put_bignum2(dh->pub_key);
1513+ first = 0;
1514+ } else {
1515+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1516+ packet_put_string(send_tok.value,
1517+ send_tok.length);
1518+ }
1519+ packet_send();
1520+ gss_release_buffer(&min_status, &send_tok);
1521+
1522+ /* If we've sent them data, they should reply */
1523+ do {
1524+ type = packet_read();
1525+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
1526+ debug("Received KEXGSS_HOSTKEY");
1527+ if (serverhostkey)
1528+ fatal("Server host key received more than once");
1529+ serverhostkey =
1530+ packet_get_string(&slen);
1531+ }
1532+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
1533+
1534+ switch (type) {
1535+ case SSH2_MSG_KEXGSS_CONTINUE:
1536+ debug("Received GSSAPI_CONTINUE");
1537+ if (maj_status == GSS_S_COMPLETE)
1538+ fatal("GSSAPI Continue received from server when complete");
1539+ recv_tok.value = packet_get_string(&strlen);
1540+ recv_tok.length = strlen;
1541+ break;
1542+ case SSH2_MSG_KEXGSS_COMPLETE:
1543+ debug("Received GSSAPI_COMPLETE");
1544+ packet_get_bignum2(dh_server_pub);
1545+ msg_tok.value = packet_get_string(&strlen);
1546+ msg_tok.length = strlen;
1547+
1548+ /* Is there a token included? */
1549+ if (packet_get_char()) {
1550+ recv_tok.value=
1551+ packet_get_string(&strlen);
1552+ recv_tok.length = strlen;
1553+ /* If we're already complete - protocol error */
1554+ if (maj_status == GSS_S_COMPLETE)
1555+ packet_disconnect("Protocol error: received token when complete");
1556+ } else {
1557+ /* No token included */
1558+ if (maj_status != GSS_S_COMPLETE)
1559+ packet_disconnect("Protocol error: did not receive final token");
1560+ }
1561+ break;
1562+ case SSH2_MSG_KEXGSS_ERROR:
1563+ debug("Received Error");
1564+ maj_status = packet_get_int();
1565+ min_status = packet_get_int();
1566+ msg = packet_get_string(NULL);
1567+ lang = packet_get_string(NULL);
1568+ fatal("GSSAPI Error: \n%.400s",msg);
1569+ default:
1570+ packet_disconnect("Protocol error: didn't expect packet type %d",
1571+ type);
1572+ }
1573+ token_ptr = &recv_tok;
1574+ } else {
1575+ /* No data, and not complete */
1576+ if (maj_status != GSS_S_COMPLETE)
1577+ fatal("Not complete, and no token output");
1578+ }
1579+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
1580+
1581+ /*
1582+ * We _must_ have received a COMPLETE message in reply from the
1583+ * server, which will have set dh_server_pub and msg_tok
1584+ */
1585+
1586+ if (type != SSH2_MSG_KEXGSS_COMPLETE)
1587+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
1588+
1589+ /* Check f in range [1, p-1] */
1590+ if (!dh_pub_is_valid(dh, dh_server_pub))
1591+ packet_disconnect("bad server public DH value");
1592+
1593+ /* compute K=f^x mod p */
1594+ klen = DH_size(dh);
1595+ kbuf = xmalloc(klen);
1596+ kout = DH_compute_key(kbuf, dh_server_pub, dh);
1597+ if (kout < 0)
1598+ fatal("DH_compute_key: failed");
1599+
1600+ shared_secret = BN_new();
1601+ if (shared_secret == NULL)
1602+ fatal("kexgss_client: BN_new failed");
1603+
1604+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
1605+ fatal("kexdh_client: BN_bin2bn failed");
1606+
1607+ memset(kbuf, 0, klen);
1608+ xfree(kbuf);
1609+
1610+ switch (kex->kex_type) {
1611+ case KEX_GSS_GRP1_SHA1:
1612+ case KEX_GSS_GRP14_SHA1:
1613+ kex_dh_hash( kex->client_version_string,
1614+ kex->server_version_string,
1615+ buffer_ptr(&kex->my), buffer_len(&kex->my),
1616+ buffer_ptr(&kex->peer), buffer_len(&kex->peer),
1617+ (serverhostkey ? serverhostkey : empty), slen,
1618+ dh->pub_key, /* e */
1619+ dh_server_pub, /* f */
1620+ shared_secret, /* K */
1621+ &hash, &hashlen
1622+ );
1623+ break;
1624+ case KEX_GSS_GEX_SHA1:
1625+ kexgex_hash(
1626+ kex->evp_md,
1627+ kex->client_version_string,
1628+ kex->server_version_string,
1629+ buffer_ptr(&kex->my), buffer_len(&kex->my),
1630+ buffer_ptr(&kex->peer), buffer_len(&kex->peer),
1631+ (serverhostkey ? serverhostkey : empty), slen,
1632+ min, nbits, max,
1633+ dh->p, dh->g,
1634+ dh->pub_key,
1635+ dh_server_pub,
1636+ shared_secret,
1637+ &hash, &hashlen
1638+ );
1639+ break;
1640+ default:
1641+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1642+ }
1643+
1644+ gssbuf.value = hash;
1645+ gssbuf.length = hashlen;
1646+
1647+ /* Verify that the hash matches the MIC we just got. */
1648+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
1649+ packet_disconnect("Hash's MIC didn't verify");
1650+
1651+ xfree(msg_tok.value);
1652+
1653+ DH_free(dh);
1654+ if (serverhostkey)
1655+ xfree(serverhostkey);
1656+ BN_clear_free(dh_server_pub);
1657+
1658+ /* save session id */
1659+ if (kex->session_id == NULL) {
1660+ kex->session_id_len = hashlen;
1661+ kex->session_id = xmalloc(kex->session_id_len);
1662+ memcpy(kex->session_id, hash, kex->session_id_len);
1663+ }
1664+
1665+ if (kex->gss_deleg_creds)
1666+ ssh_gssapi_credentials_updated(ctxt);
1667+
1668+ if (gss_kex_context == NULL)
1669+ gss_kex_context = ctxt;
1670+ else
1671+ ssh_gssapi_delete_ctx(&ctxt);
1672+
1673+ kex_derive_keys(kex, hash, hashlen, shared_secret);
1674+ BN_clear_free(shared_secret);
1675+ kex_finish(kex);
1676+}
1677+
1678+#endif /* GSSAPI */
1679Index: b/kexgsss.c
1680===================================================================
1681--- /dev/null
1682+++ b/kexgsss.c
1683@@ -0,0 +1,288 @@
1684+/*
1685+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
1686+ *
1687+ * Redistribution and use in source and binary forms, with or without
1688+ * modification, are permitted provided that the following conditions
1689+ * are met:
1690+ * 1. Redistributions of source code must retain the above copyright
1691+ * notice, this list of conditions and the following disclaimer.
1692+ * 2. Redistributions in binary form must reproduce the above copyright
1693+ * notice, this list of conditions and the following disclaimer in the
1694+ * documentation and/or other materials provided with the distribution.
1695+ *
1696+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1697+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1698+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1699+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1700+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1701+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1702+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1703+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1704+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1705+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1706+ */
1707+
1708+#include "includes.h"
1709+
1710+#ifdef GSSAPI
1711+
1712+#include <string.h>
1713+
1714+#include <openssl/crypto.h>
1715+#include <openssl/bn.h>
1716+
1717+#include "xmalloc.h"
1718+#include "buffer.h"
1719+#include "ssh2.h"
1720+#include "key.h"
1721+#include "cipher.h"
1722+#include "kex.h"
1723+#include "log.h"
1724+#include "packet.h"
1725+#include "dh.h"
1726+#include "ssh-gss.h"
1727+#include "monitor_wrap.h"
1728+#include "servconf.h"
1729+
1730+extern ServerOptions options;
1731+
1732+void
1733+kexgss_server(Kex *kex)
1734+{
1735+ OM_uint32 maj_status, min_status;
1736+
1737+ /*
1738+ * Some GSSAPI implementations use the input value of ret_flags (an
1739+ * output variable) as a means of triggering mechanism specific
1740+ * features. Initializing it to zero avoids inadvertently
1741+ * activating this non-standard behaviour.
1742+ */
1743+
1744+ OM_uint32 ret_flags = 0;
1745+ gss_buffer_desc gssbuf, recv_tok, msg_tok;
1746+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
1747+ Gssctxt *ctxt = NULL;
1748+ u_int slen, klen, kout, hashlen;
1749+ u_char *kbuf, *hash;
1750+ DH *dh;
1751+ int min = -1, max = -1, nbits = -1;
1752+ BIGNUM *shared_secret = NULL;
1753+ BIGNUM *dh_client_pub = NULL;
1754+ int type = 0;
1755+ gss_OID oid;
1756+ char *mechs;
1757+
1758+ /* Initialise GSSAPI */
1759+
1760+ /* If we're rekeying, privsep means that some of the private structures
1761+ * in the GSSAPI code are no longer available. This kludges them back
1762+ * into life
1763+ */
1764+ if (!ssh_gssapi_oid_table_ok())
1765+ if ((mechs = ssh_gssapi_server_mechanisms()))
1766+ xfree(mechs);
1767+
1768+ debug2("%s: Identifying %s", __func__, kex->name);
1769+ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
1770+ if (oid == GSS_C_NO_OID)
1771+ fatal("Unknown gssapi mechanism");
1772+
1773+ debug2("%s: Acquiring credentials", __func__);
1774+
1775+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
1776+ fatal("Unable to acquire credentials for the server");
1777+
1778+ switch (kex->kex_type) {
1779+ case KEX_GSS_GRP1_SHA1:
1780+ dh = dh_new_group1();
1781+ break;
1782+ case KEX_GSS_GRP14_SHA1:
1783+ dh = dh_new_group14();
1784+ break;
1785+ case KEX_GSS_GEX_SHA1:
1786+ debug("Doing group exchange");
1787+ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
1788+ min = packet_get_int();
1789+ nbits = packet_get_int();
1790+ max = packet_get_int();
1791+ min = MAX(DH_GRP_MIN, min);
1792+ max = MIN(DH_GRP_MAX, max);
1793+ packet_check_eom();
1794+ if (max < min || nbits < min || max < nbits)
1795+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
1796+ min, nbits, max);
1797+ dh = PRIVSEP(choose_dh(min, nbits, max));
1798+ if (dh == NULL)
1799+ packet_disconnect("Protocol error: no matching group found");
1800+
1801+ packet_start(SSH2_MSG_KEXGSS_GROUP);
1802+ packet_put_bignum2(dh->p);
1803+ packet_put_bignum2(dh->g);
1804+ packet_send();
1805+
1806+ packet_write_wait();
1807+ break;
1808+ default:
1809+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1810+ }
1811+
1812+ dh_gen_key(dh, kex->we_need * 8);
1813+
1814+ do {
1815+ debug("Wait SSH2_MSG_GSSAPI_INIT");
1816+ type = packet_read();
1817+ switch(type) {
1818+ case SSH2_MSG_KEXGSS_INIT:
1819+ if (dh_client_pub != NULL)
1820+ fatal("Received KEXGSS_INIT after initialising");
1821+ recv_tok.value = packet_get_string(&slen);
1822+ recv_tok.length = slen;
1823+
1824+ if ((dh_client_pub = BN_new()) == NULL)
1825+ fatal("dh_client_pub == NULL");
1826+
1827+ packet_get_bignum2(dh_client_pub);
1828+
1829+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
1830+ break;
1831+ case SSH2_MSG_KEXGSS_CONTINUE:
1832+ recv_tok.value = packet_get_string(&slen);
1833+ recv_tok.length = slen;
1834+ break;
1835+ default:
1836+ packet_disconnect(
1837+ "Protocol error: didn't expect packet type %d",
1838+ type);
1839+ }
1840+
1841+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
1842+ &send_tok, &ret_flags));
1843+
1844+ xfree(recv_tok.value);
1845+
1846+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
1847+ fatal("Zero length token output when incomplete");
1848+
1849+ if (dh_client_pub == NULL)
1850+ fatal("No client public key");
1851+
1852+ if (maj_status & GSS_S_CONTINUE_NEEDED) {
1853+ debug("Sending GSSAPI_CONTINUE");
1854+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1855+ packet_put_string(send_tok.value, send_tok.length);
1856+ packet_send();
1857+ gss_release_buffer(&min_status, &send_tok);
1858+ }
1859+ } while (maj_status & GSS_S_CONTINUE_NEEDED);
1860+
1861+ if (GSS_ERROR(maj_status)) {
1862+ if (send_tok.length > 0) {
1863+ packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1864+ packet_put_string(send_tok.value, send_tok.length);
1865+ packet_send();
1866+ }
1867+ fatal("accept_ctx died");
1868+ }
1869+
1870+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
1871+ fatal("Mutual Authentication flag wasn't set");
1872+
1873+ if (!(ret_flags & GSS_C_INTEG_FLAG))
1874+ fatal("Integrity flag wasn't set");
1875+
1876+ if (!dh_pub_is_valid(dh, dh_client_pub))
1877+ packet_disconnect("bad client public DH value");
1878+
1879+ klen = DH_size(dh);
1880+ kbuf = xmalloc(klen);
1881+ kout = DH_compute_key(kbuf, dh_client_pub, dh);
1882+ if (kout < 0)
1883+ fatal("DH_compute_key: failed");
1884+
1885+ shared_secret = BN_new();
1886+ if (shared_secret == NULL)
1887+ fatal("kexgss_server: BN_new failed");
1888+
1889+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
1890+ fatal("kexgss_server: BN_bin2bn failed");
1891+
1892+ memset(kbuf, 0, klen);
1893+ xfree(kbuf);
1894+
1895+ switch (kex->kex_type) {
1896+ case KEX_GSS_GRP1_SHA1:
1897+ case KEX_GSS_GRP14_SHA1:
1898+ kex_dh_hash(
1899+ kex->client_version_string, kex->server_version_string,
1900+ buffer_ptr(&kex->peer), buffer_len(&kex->peer),
1901+ buffer_ptr(&kex->my), buffer_len(&kex->my),
1902+ NULL, 0, /* Change this if we start sending host keys */
1903+ dh_client_pub, dh->pub_key, shared_secret,
1904+ &hash, &hashlen
1905+ );
1906+ break;
1907+ case KEX_GSS_GEX_SHA1:
1908+ kexgex_hash(
1909+ kex->evp_md,
1910+ kex->client_version_string, kex->server_version_string,
1911+ buffer_ptr(&kex->peer), buffer_len(&kex->peer),
1912+ buffer_ptr(&kex->my), buffer_len(&kex->my),
1913+ NULL, 0,
1914+ min, nbits, max,
1915+ dh->p, dh->g,
1916+ dh_client_pub,
1917+ dh->pub_key,
1918+ shared_secret,
1919+ &hash, &hashlen
1920+ );
1921+ break;
1922+ default:
1923+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
1924+ }
1925+
1926+ BN_clear_free(dh_client_pub);
1927+
1928+ if (kex->session_id == NULL) {
1929+ kex->session_id_len = hashlen;
1930+ kex->session_id = xmalloc(kex->session_id_len);
1931+ memcpy(kex->session_id, hash, kex->session_id_len);
1932+ }
1933+
1934+ gssbuf.value = hash;
1935+ gssbuf.length = hashlen;
1936+
1937+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
1938+ fatal("Couldn't get MIC");
1939+
1940+ packet_start(SSH2_MSG_KEXGSS_COMPLETE);
1941+ packet_put_bignum2(dh->pub_key);
1942+ packet_put_string(msg_tok.value,msg_tok.length);
1943+
1944+ if (send_tok.length != 0) {
1945+ packet_put_char(1); /* true */
1946+ packet_put_string(send_tok.value, send_tok.length);
1947+ } else {
1948+ packet_put_char(0); /* false */
1949+ }
1950+ packet_send();
1951+
1952+ gss_release_buffer(&min_status, &send_tok);
1953+ gss_release_buffer(&min_status, &msg_tok);
1954+
1955+ if (gss_kex_context == NULL)
1956+ gss_kex_context = ctxt;
1957+ else
1958+ ssh_gssapi_delete_ctx(&ctxt);
1959+
1960+ DH_free(dh);
1961+
1962+ kex_derive_keys(kex, hash, hashlen, shared_secret);
1963+ BN_clear_free(shared_secret);
1964+ kex_finish(kex);
1965+
1966+ /* If this was a rekey, then save out any delegated credentials we
1967+ * just exchanged. */
1968+ if (options.gss_store_rekey)
1969+ ssh_gssapi_rekey_creds();
1970+}
1971+#endif /* GSSAPI */
1972Index: b/key.c
1973===================================================================
1974--- a/key.c
1975+++ b/key.c
1976@@ -971,6 +971,8 @@
1977 }
1978 break;
1979 #endif /* OPENSSL_HAS_ECC */
1980+ case KEY_NULL:
1981+ return "null";
1982 }
1983 return "ssh-unknown";
1984 }
1985@@ -1276,6 +1278,8 @@
1986 strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) {
1987 return KEY_ECDSA_CERT;
1988 #endif
1989+ } else if (strcmp(name, "null") == 0) {
1990+ return KEY_NULL;
1991 }
1992
1993 debug2("key_type_from_name: unknown key type '%s'", name);
1994Index: b/key.h
1995===================================================================
1996--- a/key.h
1997+++ b/key.h
1998@@ -44,6 +44,7 @@
1999 KEY_ECDSA_CERT,
2000 KEY_RSA_CERT_V00,
2001 KEY_DSA_CERT_V00,
2002+ KEY_NULL,
2003 KEY_UNSPEC
2004 };
2005 enum fp_type {
2006Index: b/monitor.c
2007===================================================================
2008--- a/monitor.c
2009+++ b/monitor.c
2010@@ -180,6 +180,8 @@
2011 int mm_answer_gss_accept_ctx(int, Buffer *);
2012 int mm_answer_gss_userok(int, Buffer *);
2013 int mm_answer_gss_checkmic(int, Buffer *);
2014+int mm_answer_gss_sign(int, Buffer *);
2015+int mm_answer_gss_updatecreds(int, Buffer *);
2016 #endif
2017
2018 #ifdef SSH_AUDIT_EVENTS
2019@@ -251,6 +253,7 @@
2020 {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
2021 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
2022 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
2023+ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
2024 #endif
2025 #ifdef JPAKE
2026 {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata},
2027@@ -263,6 +266,12 @@
2028 };
2029
2030 struct mon_table mon_dispatch_postauth20[] = {
2031+#ifdef GSSAPI
2032+ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
2033+ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
2034+ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
2035+ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
2036+#endif
2037 {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
2038 {MONITOR_REQ_SIGN, 0, mm_answer_sign},
2039 {MONITOR_REQ_PTY, 0, mm_answer_pty},
2040@@ -371,6 +380,10 @@
2041 /* Permit requests for moduli and signatures */
2042 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
2043 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
2044+#ifdef GSSAPI
2045+ /* and for the GSSAPI key exchange */
2046+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
2047+#endif
2048 } else {
2049 mon_dispatch = mon_dispatch_proto15;
2050
2051@@ -468,6 +481,10 @@
2052 monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
2053 monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
2054 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
2055+#ifdef GSSAPI
2056+ /* and for the GSSAPI key exchange */
2057+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
2058+#endif
2059 } else {
2060 mon_dispatch = mon_dispatch_postauth15;
2061 monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
2062@@ -1802,6 +1819,13 @@
2063 kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
2064 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
2065 kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
2066+#ifdef GSSAPI
2067+ if (options.gss_keyex) {
2068+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
2069+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
2070+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
2071+ }
2072+#endif
2073 kex->server = 1;
2074 kex->hostkey_type = buffer_get_int(m);
2075 kex->kex_type = buffer_get_int(m);
2076@@ -2008,6 +2032,9 @@
2077 OM_uint32 major;
2078 u_int len;
2079
2080+ if (!options.gss_authentication && !options.gss_keyex)
2081+ fatal("In GSSAPI monitor when GSSAPI is disabled");
2082+
2083 goid.elements = buffer_get_string(m, &len);
2084 goid.length = len;
2085
2086@@ -2035,6 +2062,9 @@
2087 OM_uint32 flags = 0; /* GSI needs this */
2088 u_int len;
2089
2090+ if (!options.gss_authentication && !options.gss_keyex)
2091+ fatal("In GSSAPI monitor when GSSAPI is disabled");
2092+
2093 in.value = buffer_get_string(m, &len);
2094 in.length = len;
2095 major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
2096@@ -2052,6 +2082,7 @@
2097 monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
2098 monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
2099 monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
2100+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
2101 }
2102 return (0);
2103 }
2104@@ -2063,6 +2094,9 @@
2105 OM_uint32 ret;
2106 u_int len;
2107
2108+ if (!options.gss_authentication && !options.gss_keyex)
2109+ fatal("In GSSAPI monitor when GSSAPI is disabled");
2110+
2111 gssbuf.value = buffer_get_string(m, &len);
2112 gssbuf.length = len;
2113 mic.value = buffer_get_string(m, &len);
2114@@ -2089,7 +2123,11 @@
2115 {
2116 int authenticated;
2117
2118- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
2119+ if (!options.gss_authentication && !options.gss_keyex)
2120+ fatal("In GSSAPI monitor when GSSAPI is disabled");
2121+
2122+ authenticated = authctxt->valid &&
2123+ ssh_gssapi_userok(authctxt->user, authctxt->pw);
2124
2125 buffer_clear(m);
2126 buffer_put_int(m, authenticated);
2127@@ -2102,6 +2140,74 @@
2128 /* Monitor loop will terminate if authenticated */
2129 return (authenticated);
2130 }
2131+
2132+int
2133+mm_answer_gss_sign(int socket, Buffer *m)
2134+{
2135+ gss_buffer_desc data;
2136+ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
2137+ OM_uint32 major, minor;
2138+ u_int len;
2139+
2140+ if (!options.gss_authentication && !options.gss_keyex)
2141+ fatal("In GSSAPI monitor when GSSAPI is disabled");
2142+
2143+ data.value = buffer_get_string(m, &len);
2144+ data.length = len;
2145+ if (data.length != 20)
2146+ fatal("%s: data length incorrect: %d", __func__,
2147+ (int) data.length);
2148+
2149+ /* Save the session ID on the first time around */
2150+ if (session_id2_len == 0) {
2151+ session_id2_len = data.length;
2152+ session_id2 = xmalloc(session_id2_len);
2153+ memcpy(session_id2, data.value, session_id2_len);
2154+ }
2155+ major = ssh_gssapi_sign(gsscontext, &data, &hash);
2156+
2157+ xfree(data.value);
2158+
2159+ buffer_clear(m);
2160+ buffer_put_int(m, major);
2161+ buffer_put_string(m, hash.value, hash.length);
2162+
2163+ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
2164+
2165+ gss_release_buffer(&minor, &hash);
2166+
2167+ /* Turn on getpwnam permissions */
2168+ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
2169+
2170+ /* And credential updating, for when rekeying */
2171+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
2172+
2173+ return (0);
2174+}
2175+
2176+int
2177+mm_answer_gss_updatecreds(int socket, Buffer *m) {
2178+ ssh_gssapi_ccache store;
2179+ int ok;
2180+
2181+ store.filename = buffer_get_string(m, NULL);
2182+ store.envvar = buffer_get_string(m, NULL);
2183+ store.envval = buffer_get_string(m, NULL);
2184+
2185+ ok = ssh_gssapi_update_creds(&store);
2186+
2187+ xfree(store.filename);
2188+ xfree(store.envvar);
2189+ xfree(store.envval);
2190+
2191+ buffer_clear(m);
2192+ buffer_put_int(m, ok);
2193+
2194+ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
2195+
2196+ return(0);
2197+}
2198+
2199 #endif /* GSSAPI */
2200
2201 #ifdef JPAKE
2202Index: b/monitor.h
2203===================================================================
2204--- a/monitor.h
2205+++ b/monitor.h
2206@@ -53,6 +53,8 @@
2207 MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP,
2208 MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK,
2209 MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC,
2210+ MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN,
2211+ MONITOR_REQ_GSSUPCREDS, MONITOR_ANS_GSSUPCREDS,
2212 MONITOR_REQ_PAM_START,
2213 MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT,
2214 MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX,
2215Index: b/monitor_wrap.c
2216===================================================================
2217--- a/monitor_wrap.c
2218+++ b/monitor_wrap.c
2219@@ -1270,7 +1270,7 @@
2220 }
2221
2222 int
2223-mm_ssh_gssapi_userok(char *user)
2224+mm_ssh_gssapi_userok(char *user, struct passwd *pw)
2225 {
2226 Buffer m;
2227 int authenticated = 0;
2228@@ -1287,6 +1287,51 @@
2229 debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
2230 return (authenticated);
2231 }
2232+
2233+OM_uint32
2234+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
2235+{
2236+ Buffer m;
2237+ OM_uint32 major;
2238+ u_int len;
2239+
2240+ buffer_init(&m);
2241+ buffer_put_string(&m, data->value, data->length);
2242+
2243+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
2244+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
2245+
2246+ major = buffer_get_int(&m);
2247+ hash->value = buffer_get_string(&m, &len);
2248+ hash->length = len;
2249+
2250+ buffer_free(&m);
2251+
2252+ return(major);
2253+}
2254+
2255+int
2256+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
2257+{
2258+ Buffer m;
2259+ int ok;
2260+
2261+ buffer_init(&m);
2262+
2263+ buffer_put_cstring(&m, store->filename ? store->filename : "");
2264+ buffer_put_cstring(&m, store->envvar ? store->envvar : "");
2265+ buffer_put_cstring(&m, store->envval ? store->envval : "");
2266+
2267+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
2268+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
2269+
2270+ ok = buffer_get_int(&m);
2271+
2272+ buffer_free(&m);
2273+
2274+ return (ok);
2275+}
2276+
2277 #endif /* GSSAPI */
2278
2279 #ifdef JPAKE
2280Index: b/monitor_wrap.h
2281===================================================================
2282--- a/monitor_wrap.h
2283+++ b/monitor_wrap.h
2284@@ -58,8 +58,10 @@
2285 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
2286 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
2287 gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
2288-int mm_ssh_gssapi_userok(char *user);
2289+int mm_ssh_gssapi_userok(char *user, struct passwd *);
2290 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
2291+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
2292+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
2293 #endif
2294
2295 #ifdef USE_PAM
2296Index: b/readconf.c
2297===================================================================
2298--- a/readconf.c
2299+++ b/readconf.c
2300@@ -129,6 +129,8 @@
2301 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
2302 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
2303 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
2304+ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
2305+ oGssServerIdentity,
2306 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
2307 oSendEnv, oControlPath, oControlMaster, oControlPersist,
2308 oHashKnownHosts,
2309@@ -169,10 +171,19 @@
2310 { "afstokenpassing", oUnsupported },
2311 #if defined(GSSAPI)
2312 { "gssapiauthentication", oGssAuthentication },
2313+ { "gssapikeyexchange", oGssKeyEx },
2314 { "gssapidelegatecredentials", oGssDelegateCreds },
2315+ { "gssapitrustdns", oGssTrustDns },
2316+ { "gssapiclientidentity", oGssClientIdentity },
2317+ { "gssapiserveridentity", oGssServerIdentity },
2318+ { "gssapirenewalforcesrekey", oGssRenewalRekey },
2319 #else
2320 { "gssapiauthentication", oUnsupported },
2321+ { "gssapikeyexchange", oUnsupported },
2322 { "gssapidelegatecredentials", oUnsupported },
2323+ { "gssapitrustdns", oUnsupported },
2324+ { "gssapiclientidentity", oUnsupported },
2325+ { "gssapirenewalforcesrekey", oUnsupported },
2326 #endif
2327 { "fallbacktorsh", oDeprecated },
2328 { "usersh", oDeprecated },
2329@@ -482,10 +493,30 @@
2330 intptr = &options->gss_authentication;
2331 goto parse_flag;
2332
2333+ case oGssKeyEx:
2334+ intptr = &options->gss_keyex;
2335+ goto parse_flag;
2336+
2337 case oGssDelegateCreds:
2338 intptr = &options->gss_deleg_creds;
2339 goto parse_flag;
2340
2341+ case oGssTrustDns:
2342+ intptr = &options->gss_trust_dns;
2343+ goto parse_flag;
2344+
2345+ case oGssClientIdentity:
2346+ charptr = &options->gss_client_identity;
2347+ goto parse_string;
2348+
2349+ case oGssServerIdentity:
2350+ charptr = &options->gss_server_identity;
2351+ goto parse_string;
2352+
2353+ case oGssRenewalRekey:
2354+ intptr = &options->gss_renewal_rekey;
2355+ goto parse_flag;
2356+
2357 case oBatchMode:
2358 intptr = &options->batch_mode;
2359 goto parse_flag;
2360@@ -1138,7 +1169,12 @@
2361 options->pubkey_authentication = -1;
2362 options->challenge_response_authentication = -1;
2363 options->gss_authentication = -1;
2364+ options->gss_keyex = -1;
2365 options->gss_deleg_creds = -1;
2366+ options->gss_trust_dns = -1;
2367+ options->gss_renewal_rekey = -1;
2368+ options->gss_client_identity = NULL;
2369+ options->gss_server_identity = NULL;
2370 options->password_authentication = -1;
2371 options->kbd_interactive_authentication = -1;
2372 options->kbd_interactive_devices = NULL;
2373@@ -1238,8 +1274,14 @@
2374 options->challenge_response_authentication = 1;
2375 if (options->gss_authentication == -1)
2376 options->gss_authentication = 0;
2377+ if (options->gss_keyex == -1)
2378+ options->gss_keyex = 0;
2379 if (options->gss_deleg_creds == -1)
2380 options->gss_deleg_creds = 0;
2381+ if (options->gss_trust_dns == -1)
2382+ options->gss_trust_dns = 0;
2383+ if (options->gss_renewal_rekey == -1)
2384+ options->gss_renewal_rekey = 0;
2385 if (options->password_authentication == -1)
2386 options->password_authentication = 1;
2387 if (options->kbd_interactive_authentication == -1)
2388Index: b/readconf.h
2389===================================================================
2390--- a/readconf.h
2391+++ b/readconf.h
2392@@ -47,7 +47,12 @@
2393 int challenge_response_authentication;
2394 /* Try S/Key or TIS, authentication. */
2395 int gss_authentication; /* Try GSS authentication */
2396+ int gss_keyex; /* Try GSS key exchange */
2397 int gss_deleg_creds; /* Delegate GSS credentials */
2398+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */
2399+ int gss_renewal_rekey; /* Credential renewal forces rekey */
2400+ char *gss_client_identity; /* Principal to initiate GSSAPI with */
2401+ char *gss_server_identity; /* GSSAPI target principal */
2402 int password_authentication; /* Try password
2403 * authentication. */
2404 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
2405Index: b/servconf.c
2406===================================================================
2407--- a/servconf.c
2408+++ b/servconf.c
2409@@ -97,7 +97,10 @@
2410 options->kerberos_ticket_cleanup = -1;
2411 options->kerberos_get_afs_token = -1;
2412 options->gss_authentication=-1;
2413+ options->gss_keyex = -1;
2414 options->gss_cleanup_creds = -1;
2415+ options->gss_strict_acceptor = -1;
2416+ options->gss_store_rekey = -1;
2417 options->password_authentication = -1;
2418 options->kbd_interactive_authentication = -1;
2419 options->challenge_response_authentication = -1;
2420@@ -225,8 +228,14 @@
2421 options->kerberos_get_afs_token = 0;
2422 if (options->gss_authentication == -1)
2423 options->gss_authentication = 0;
2424+ if (options->gss_keyex == -1)
2425+ options->gss_keyex = 0;
2426 if (options->gss_cleanup_creds == -1)
2427 options->gss_cleanup_creds = 1;
2428+ if (options->gss_strict_acceptor == -1)
2429+ options->gss_strict_acceptor = 1;
2430+ if (options->gss_store_rekey == -1)
2431+ options->gss_store_rekey = 0;
2432 if (options->password_authentication == -1)
2433 options->password_authentication = 1;
2434 if (options->kbd_interactive_authentication == -1)
2435@@ -318,7 +327,9 @@
2436 sBanner, sUseDNS, sHostbasedAuthentication,
2437 sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
2438 sClientAliveCountMax, sAuthorizedKeysFile,
2439- sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
2440+ sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
2441+ sGssKeyEx, sGssStoreRekey,
2442+ sAcceptEnv, sPermitTunnel,
2443 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
2444 sUsePrivilegeSeparation, sAllowAgentForwarding,
2445 sZeroKnowledgePasswordAuthentication, sHostCertificate,
2446@@ -382,10 +393,20 @@
2447 #ifdef GSSAPI
2448 { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
2449 { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
2450+ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
2451+ { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
2452+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
2453+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
2454 #else
2455 { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
2456 { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
2457+ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
2458+ { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
2459+ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
2460+ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
2461 #endif
2462+ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
2463+ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
2464 { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
2465 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
2466 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
2467@@ -962,10 +983,22 @@
2468 intptr = &options->gss_authentication;
2469 goto parse_flag;
2470
2471+ case sGssKeyEx:
2472+ intptr = &options->gss_keyex;
2473+ goto parse_flag;
2474+
2475 case sGssCleanupCreds:
2476 intptr = &options->gss_cleanup_creds;
2477 goto parse_flag;
2478
2479+ case sGssStrictAcceptor:
2480+ intptr = &options->gss_strict_acceptor;
2481+ goto parse_flag;
2482+
2483+ case sGssStoreRekey:
2484+ intptr = &options->gss_store_rekey;
2485+ goto parse_flag;
2486+
2487 case sPasswordAuthentication:
2488 intptr = &options->password_authentication;
2489 goto parse_flag;
2490@@ -1720,7 +1753,10 @@
2491 #endif
2492 #ifdef GSSAPI
2493 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
2494+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex);
2495 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
2496+ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor);
2497+ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey);
2498 #endif
2499 #ifdef JPAKE
2500 dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication,
2501Index: b/servconf.h
2502===================================================================
2503--- a/servconf.h
2504+++ b/servconf.h
2505@@ -103,7 +103,10 @@
2506 int kerberos_get_afs_token; /* If true, try to get AFS token if
2507 * authenticated with Kerberos. */
2508 int gss_authentication; /* If true, permit GSSAPI authentication */
2509+ int gss_keyex; /* If true, permit GSSAPI key exchange */
2510 int gss_cleanup_creds; /* If true, destroy cred cache on logout */
2511+ int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */
2512+ int gss_store_rekey;
2513 int password_authentication; /* If true, permit password
2514 * authentication. */
2515 int kbd_interactive_authentication; /* If true, permit */
2516Index: b/ssh-gss.h
2517===================================================================
2518--- a/ssh-gss.h
2519+++ b/ssh-gss.h
2520@@ -1,6 +1,6 @@
2521 /* $OpenBSD: ssh-gss.h,v 1.10 2007/06/12 08:20:00 djm Exp $ */
2522 /*
2523- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
2524+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
2525 *
2526 * Redistribution and use in source and binary forms, with or without
2527 * modification, are permitted provided that the following conditions
2528@@ -60,10 +60,22 @@
2529
2530 #define SSH_GSS_OIDTYPE 0x06
2531
2532+#define SSH2_MSG_KEXGSS_INIT 30
2533+#define SSH2_MSG_KEXGSS_CONTINUE 31
2534+#define SSH2_MSG_KEXGSS_COMPLETE 32
2535+#define SSH2_MSG_KEXGSS_HOSTKEY 33
2536+#define SSH2_MSG_KEXGSS_ERROR 34
2537+#define SSH2_MSG_KEXGSS_GROUPREQ 40
2538+#define SSH2_MSG_KEXGSS_GROUP 41
2539+#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-"
2540+#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-"
2541+#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-"
2542+
2543 typedef struct {
2544 char *filename;
2545 char *envvar;
2546 char *envval;
2547+ struct passwd *owner;
2548 void *data;
2549 } ssh_gssapi_ccache;
2550
2551@@ -71,8 +83,11 @@
2552 gss_buffer_desc displayname;
2553 gss_buffer_desc exportedname;
2554 gss_cred_id_t creds;
2555+ gss_name_t name;
2556 struct ssh_gssapi_mech_struct *mech;
2557 ssh_gssapi_ccache store;
2558+ int used;
2559+ int updated;
2560 } ssh_gssapi_client;
2561
2562 typedef struct ssh_gssapi_mech_struct {
2563@@ -83,6 +98,7 @@
2564 int (*userok) (ssh_gssapi_client *, char *);
2565 int (*localname) (ssh_gssapi_client *, char **);
2566 void (*storecreds) (ssh_gssapi_client *);
2567+ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
2568 } ssh_gssapi_mech;
2569
2570 typedef struct {
2571@@ -93,10 +109,11 @@
2572 gss_OID oid; /* client */
2573 gss_cred_id_t creds; /* server */
2574 gss_name_t client; /* server */
2575- gss_cred_id_t client_creds; /* server */
2576+ gss_cred_id_t client_creds; /* both */
2577 } Gssctxt;
2578
2579 extern ssh_gssapi_mech *supported_mechs[];
2580+extern Gssctxt *gss_kex_context;
2581
2582 int ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
2583 void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
2584@@ -116,16 +133,30 @@
2585 void ssh_gssapi_delete_ctx(Gssctxt **);
2586 OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
2587 void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
2588-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
2589+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
2590+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
2591+int ssh_gssapi_credentials_updated(Gssctxt *);
2592
2593 /* In the server */
2594+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
2595+ const char *);
2596+char *ssh_gssapi_client_mechanisms(const char *, const char *);
2597+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
2598+ const char *);
2599+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
2600+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *,
2601+ const char *);
2602 OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
2603-int ssh_gssapi_userok(char *name);
2604+int ssh_gssapi_userok(char *name, struct passwd *);
2605 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
2606 void ssh_gssapi_do_child(char ***, u_int *);
2607 void ssh_gssapi_cleanup_creds(void);
2608 void ssh_gssapi_storecreds(void);
2609
2610+char *ssh_gssapi_server_mechanisms(void);
2611+int ssh_gssapi_oid_table_ok();
2612+
2613+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
2614 #endif /* GSSAPI */
2615
2616 #endif /* _SSH_GSS_H */
2617Index: b/ssh_config
2618===================================================================
2619--- a/ssh_config
2620+++ b/ssh_config
2621@@ -26,6 +26,8 @@
2622 # HostbasedAuthentication no
2623 # GSSAPIAuthentication no
2624 # GSSAPIDelegateCredentials no
2625+# GSSAPIKeyExchange no
2626+# GSSAPITrustDNS no
2627 # BatchMode no
2628 # CheckHostIP yes
2629 # AddressFamily any
2630Index: b/ssh_config.5
2631===================================================================
2632--- a/ssh_config.5
2633+++ b/ssh_config.5
2634@@ -527,11 +527,43 @@
2635 The default is
2636 .Dq no .
2637 Note that this option applies to protocol version 2 only.
2638+.It Cm GSSAPIKeyExchange
2639+Specifies whether key exchange based on GSSAPI may be used. When using
2640+GSSAPI key exchange the server need not have a host key.
2641+The default is
2642+.Dq no .
2643+Note that this option applies to protocol version 2 only.
2644+.It Cm GSSAPIClientIdentity
2645+If set, specifies the GSSAPI client identity that ssh should use when
2646+connecting to the server. The default is unset, which means that the default
2647+identity will be used.
2648+.It Cm GSSAPIServerIdentity
2649+If set, specifies the GSSAPI server identity that ssh should expect when
2650+connecting to the server. The default is unset, which means that the
2651+expected GSSAPI server identity will be determined from the target
2652+hostname.
2653 .It Cm GSSAPIDelegateCredentials
2654 Forward (delegate) credentials to the server.
2655 The default is
2656 .Dq no .
2657-Note that this option applies to protocol version 2 only.
2658+Note that this option applies to protocol version 2 connections using GSSAPI.
2659+.It Cm GSSAPIRenewalForcesRekey
2660+If set to
2661+.Dq yes
2662+then renewal of the client's GSSAPI credentials will force the rekeying of the
2663+ssh connection. With a compatible server, this can delegate the renewed
2664+credentials to a session on the server.
2665+The default is
2666+.Dq no .
2667+.It Cm GSSAPITrustDns
2668+Set to
2669+.Dq yes to indicate that the DNS is trusted to securely canonicalize
2670+the name of the host being connected to. If
2671+.Dq no, the hostname entered on the
2672+command line will be passed untouched to the GSSAPI library.
2673+The default is
2674+.Dq no .
2675+This option only applies to protocol version 2 connections using GSSAPI.
2676 .It Cm HashKnownHosts
2677 Indicates that
2678 .Xr ssh 1
2679Index: b/sshconnect2.c
2680===================================================================
2681--- a/sshconnect2.c
2682+++ b/sshconnect2.c
2683@@ -160,9 +160,34 @@
2684 {
2685 Kex *kex;
2686
2687+#ifdef GSSAPI
2688+ char *orig = NULL, *gss = NULL;
2689+ char *gss_host = NULL;
2690+#endif
2691+
2692 xxx_host = host;
2693 xxx_hostaddr = hostaddr;
2694
2695+#ifdef GSSAPI
2696+ if (options.gss_keyex) {
2697+ /* Add the GSSAPI mechanisms currently supported on this
2698+ * client to the key exchange algorithm proposal */
2699+ orig = myproposal[PROPOSAL_KEX_ALGS];
2700+
2701+ if (options.gss_trust_dns)
2702+ gss_host = (char *)get_canonical_hostname(1);
2703+ else
2704+ gss_host = host;
2705+
2706+ gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity);
2707+ if (gss) {
2708+ debug("Offering GSSAPI proposal: %s", gss);
2709+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS],
2710+ "%s,%s", gss, orig);
2711+ }
2712+ }
2713+#endif
2714+
2715 if (options.ciphers == (char *)-1) {
2716 logit("No valid ciphers for protocol version 2 given, using defaults.");
2717 options.ciphers = NULL;
2718@@ -197,6 +222,17 @@
2719 if (options.kex_algorithms != NULL)
2720 myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
2721
2722+#ifdef GSSAPI
2723+ /* If we've got GSSAPI algorithms, then we also support the
2724+ * 'null' hostkey, as a last resort */
2725+ if (options.gss_keyex && gss) {
2726+ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
2727+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
2728+ "%s,null", orig);
2729+ xfree(gss);
2730+ }
2731+#endif
2732+
2733 if (options.rekey_limit)
2734 packet_set_rekey_limit((u_int32_t)options.rekey_limit);
2735
2736@@ -207,10 +243,30 @@
2737 kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
2738 kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
2739 kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
2740+#ifdef GSSAPI
2741+ if (options.gss_keyex) {
2742+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
2743+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
2744+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client;
2745+ }
2746+#endif
2747 kex->client_version_string=client_version_string;
2748 kex->server_version_string=server_version_string;
2749 kex->verify_host_key=&verify_host_key_callback;
2750
2751+#ifdef GSSAPI
2752+ if (options.gss_keyex) {
2753+ kex->gss_deleg_creds = options.gss_deleg_creds;
2754+ kex->gss_trust_dns = options.gss_trust_dns;
2755+ kex->gss_client = options.gss_client_identity;
2756+ if (options.gss_server_identity) {
2757+ kex->gss_host = options.gss_server_identity;
2758+ } else {
2759+ kex->gss_host = gss_host;
2760+ }
2761+ }
2762+#endif
2763+
2764 xxx_kex = kex;
2765
2766 dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
2767@@ -305,6 +361,7 @@
2768 void input_gssapi_hash(int type, u_int32_t, void *);
2769 void input_gssapi_error(int, u_int32_t, void *);
2770 void input_gssapi_errtok(int, u_int32_t, void *);
2771+int userauth_gsskeyex(Authctxt *authctxt);
2772 #endif
2773
2774 void userauth(Authctxt *, char *);
2775@@ -320,6 +377,11 @@
2776
2777 Authmethod authmethods[] = {
2778 #ifdef GSSAPI
2779+ {"gssapi-keyex",
2780+ userauth_gsskeyex,
2781+ NULL,
2782+ &options.gss_authentication,
2783+ NULL},
2784 {"gssapi-with-mic",
2785 userauth_gssapi,
2786 NULL,
2787@@ -626,19 +688,31 @@
2788 static u_int mech = 0;
2789 OM_uint32 min;
2790 int ok = 0;
2791+ const char *gss_host;
2792+
2793+ if (options.gss_server_identity)
2794+ gss_host = options.gss_server_identity;
2795+ else if (options.gss_trust_dns)
2796+ gss_host = get_canonical_hostname(1);
2797+ else
2798+ gss_host = authctxt->host;
2799
2800 /* Try one GSSAPI method at a time, rather than sending them all at
2801 * once. */
2802
2803 if (gss_supported == NULL)
2804- gss_indicate_mechs(&min, &gss_supported);
2805+ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) {
2806+ gss_supported = NULL;
2807+ return 0;
2808+ }
2809
2810 /* Check to see if the mechanism is usable before we offer it */
2811 while (mech < gss_supported->count && !ok) {
2812 /* My DER encoding requires length<128 */
2813 if (gss_supported->elements[mech].length < 128 &&
2814 ssh_gssapi_check_mechanism(&gssctxt,
2815- &gss_supported->elements[mech], authctxt->host)) {
2816+ &gss_supported->elements[mech], gss_host,
2817+ options.gss_client_identity)) {
2818 ok = 1; /* Mechanism works */
2819 } else {
2820 mech++;
2821@@ -735,8 +809,8 @@
2822 {
2823 Authctxt *authctxt = ctxt;
2824 Gssctxt *gssctxt;
2825- int oidlen;
2826- char *oidv;
2827+ u_int oidlen;
2828+ u_char *oidv;
2829
2830 if (authctxt == NULL)
2831 fatal("input_gssapi_response: no authentication context");
2832@@ -846,6 +920,48 @@
2833 xfree(msg);
2834 xfree(lang);
2835 }
2836+
2837+int
2838+userauth_gsskeyex(Authctxt *authctxt)
2839+{
2840+ Buffer b;
2841+ gss_buffer_desc gssbuf;
2842+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
2843+ OM_uint32 ms;
2844+
2845+ static int attempt = 0;
2846+ if (attempt++ >= 1)
2847+ return (0);
2848+
2849+ if (gss_kex_context == NULL) {
2850+ debug("No valid Key exchange context");
2851+ return (0);
2852+ }
2853+
2854+ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service,
2855+ "gssapi-keyex");
2856+
2857+ gssbuf.value = buffer_ptr(&b);
2858+ gssbuf.length = buffer_len(&b);
2859+
2860+ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
2861+ buffer_free(&b);
2862+ return (0);
2863+ }
2864+
2865+ packet_start(SSH2_MSG_USERAUTH_REQUEST);
2866+ packet_put_cstring(authctxt->server_user);
2867+ packet_put_cstring(authctxt->service);
2868+ packet_put_cstring(authctxt->method->name);
2869+ packet_put_string(mic.value, mic.length);
2870+ packet_send();
2871+
2872+ buffer_free(&b);
2873+ gss_release_buffer(&ms, &mic);
2874+
2875+ return (1);
2876+}
2877+
2878 #endif /* GSSAPI */
2879
2880 int
2881Index: b/sshd.c
2882===================================================================
2883--- a/sshd.c
2884+++ b/sshd.c
2885@@ -121,6 +121,10 @@
2886 #include "ssh-sandbox.h"
2887 #include "version.h"
2888
2889+#ifdef USE_SECURITY_SESSION_API
2890+#include <Security/AuthSession.h>
2891+#endif
2892+
2893 #ifdef LIBWRAP
2894 #include <tcpd.h>
2895 #include <syslog.h>
2896@@ -1612,10 +1616,13 @@
2897 logit("Disabling protocol version 1. Could not load host key");
2898 options.protocol &= ~SSH_PROTO_1;
2899 }
2900+#ifndef GSSAPI
2901+ /* The GSSAPI key exchange can run without a host key */
2902 if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
2903 logit("Disabling protocol version 2. Could not load host key");
2904 options.protocol &= ~SSH_PROTO_2;
2905 }
2906+#endif
2907 if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
2908 logit("sshd: no hostkeys available -- exiting.");
2909 exit(1);
2910@@ -1944,6 +1951,60 @@
2911 /* Log the connection. */
2912 verbose("Connection from %.500s port %d", remote_ip, remote_port);
2913
2914+#ifdef USE_SECURITY_SESSION_API
2915+ /*
2916+ * Create a new security session for use by the new user login if
2917+ * the current session is the root session or we are not launched
2918+ * by inetd (eg: debugging mode or server mode). We do not
2919+ * necessarily need to create a session if we are launched from
2920+ * inetd because Panther xinetd will create a session for us.
2921+ *
2922+ * The only case where this logic will fail is if there is an
2923+ * inetd running in a non-root session which is not creating
2924+ * new sessions for us. Then all the users will end up in the
2925+ * same session (bad).
2926+ *
2927+ * When the client exits, the session will be destroyed for us
2928+ * automatically.
2929+ *
2930+ * We must create the session before any credentials are stored
2931+ * (including AFS pags, which happens a few lines below).
2932+ */
2933+ {
2934+ OSStatus err = 0;
2935+ SecuritySessionId sid = 0;
2936+ SessionAttributeBits sattrs = 0;
2937+
2938+ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
2939+ if (err)
2940+ error("SessionGetInfo() failed with error %.8X",
2941+ (unsigned) err);
2942+ else
2943+ debug("Current Session ID is %.8X / Session Attributes are %.8X",
2944+ (unsigned) sid, (unsigned) sattrs);
2945+
2946+ if (inetd_flag && !(sattrs & sessionIsRoot))
2947+ debug("Running in inetd mode in a non-root session... "
2948+ "assuming inetd created the session for us.");
2949+ else {
2950+ debug("Creating new security session...");
2951+ err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
2952+ if (err)
2953+ error("SessionCreate() failed with error %.8X",
2954+ (unsigned) err);
2955+
2956+ err = SessionGetInfo(callerSecuritySession, &sid,
2957+ &sattrs);
2958+ if (err)
2959+ error("SessionGetInfo() failed with error %.8X",
2960+ (unsigned) err);
2961+ else
2962+ debug("New Session ID is %.8X / Session Attributes are %.8X",
2963+ (unsigned) sid, (unsigned) sattrs);
2964+ }
2965+ }
2966+#endif
2967+
2968 /*
2969 * We don't want to listen forever unless the other side
2970 * successfully authenticates itself. So we set up an alarm which is
2971@@ -2325,6 +2386,48 @@
2972
2973 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
2974
2975+#ifdef GSSAPI
2976+ {
2977+ char *orig;
2978+ char *gss = NULL;
2979+ char *newstr = NULL;
2980+ orig = myproposal[PROPOSAL_KEX_ALGS];
2981+
2982+ /*
2983+ * If we don't have a host key, then there's no point advertising
2984+ * the other key exchange algorithms
2985+ */
2986+
2987+ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0)
2988+ orig = NULL;
2989+
2990+ if (options.gss_keyex)
2991+ gss = ssh_gssapi_server_mechanisms();
2992+ else
2993+ gss = NULL;
2994+
2995+ if (gss && orig)
2996+ xasprintf(&newstr, "%s,%s", gss, orig);
2997+ else if (gss)
2998+ newstr = gss;
2999+ else if (orig)
3000+ newstr = orig;
3001+
3002+ /*
3003+ * If we've got GSSAPI mechanisms, then we've got the 'null' host
3004+ * key alg, but we can't tell people about it unless its the only
3005+ * host key algorithm we support
3006+ */
3007+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0)
3008+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null";
3009+
3010+ if (newstr)
3011+ myproposal[PROPOSAL_KEX_ALGS] = newstr;
3012+ else
3013+ fatal("No supported key exchange algorithms");
3014+ }
3015+#endif
3016+
3017 /* start key exchange */
3018 kex = kex_setup(myproposal);
3019 kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
3020@@ -2332,6 +2435,13 @@
3021 kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
3022 kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
3023 kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
3024+#ifdef GSSAPI
3025+ if (options.gss_keyex) {
3026+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
3027+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
3028+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
3029+ }
3030+#endif
3031 kex->server = 1;
3032 kex->client_version_string=client_version_string;
3033 kex->server_version_string=server_version_string;
3034Index: b/sshd_config
3035===================================================================
3036--- a/sshd_config
3037+++ b/sshd_config
3038@@ -75,6 +75,8 @@
3039 # GSSAPI options
3040 #GSSAPIAuthentication no
3041 #GSSAPICleanupCredentials yes
3042+#GSSAPIStrictAcceptorCheck yes
3043+#GSSAPIKeyExchange no
3044
3045 # Set this to 'yes' to enable PAM authentication, account processing,
3046 # and session processing. If this is enabled, PAM authentication will
3047Index: b/sshd_config.5
3048===================================================================
3049--- a/sshd_config.5
3050+++ b/sshd_config.5
3051@@ -424,12 +424,40 @@
3052 The default is
3053 .Dq no .
3054 Note that this option applies to protocol version 2 only.
3055+.It Cm GSSAPIKeyExchange
3056+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
3057+doesn't rely on ssh keys to verify host identity.
3058+The default is
3059+.Dq no .
3060+Note that this option applies to protocol version 2 only.
3061 .It Cm GSSAPICleanupCredentials
3062 Specifies whether to automatically destroy the user's credentials cache
3063 on logout.
3064 The default is
3065 .Dq yes .
3066 Note that this option applies to protocol version 2 only.
3067+.It Cm GSSAPIStrictAcceptorCheck
3068+Determines whether to be strict about the identity of the GSSAPI acceptor
3069+a client authenticates against. If
3070+.Dq yes
3071+then the client must authenticate against the
3072+.Pa host
3073+service on the current hostname. If
3074+.Dq no
3075+then the client may authenticate against any service key stored in the
3076+machine's default store. This facility is provided to assist with operation
3077+on multi homed machines.
3078+The default is
3079+.Dq yes .
3080+Note that this option applies only to protocol version 2 GSSAPI connections,
3081+and setting it to
3082+.Dq no
3083+may only work with recent Kerberos GSSAPI libraries.
3084+.It Cm GSSAPIStoreCredentialsOnRekey
3085+Controls whether the user's GSSAPI credentials should be updated following a
3086+successful connection rekeying. This option can be used to accepted renewed
3087+or updated credentials from a compatible client. The default is
3088+.Dq no .
3089 .It Cm HostbasedAuthentication
3090 Specifies whether rhosts or /etc/hosts.equiv authentication together
3091 with successful public key client host authentication is allowed
diff --git a/debian/patches/helpful-wait-terminate.patch b/debian/patches/helpful-wait-terminate.patch
new file mode 100644
index 000000000..857f86456
--- /dev/null
+++ b/debian/patches/helpful-wait-terminate.patch
@@ -0,0 +1,18 @@
1Description: Mention ~& when waiting for forwarded connections to terminate
2Author: Matthew Vernon <matthew@debian.org>
3Bug-Debian: http://bugs.debian.org/50308
4Last-Update: 2010-02-27
5
6Index: b/serverloop.c
7===================================================================
8--- a/serverloop.c
9+++ b/serverloop.c
10@@ -680,7 +680,7 @@
11 if (!channel_still_open())
12 break;
13 if (!waiting_termination) {
14- const char *s = "Waiting for forwarded connections to terminate...\r\n";
15+ const char *s = "Waiting for forwarded connections to terminate... (press ~& to background)\r\n";
16 char *cp;
17 waiting_termination = 1;
18 buffer_append(&stderr_buffer, s, strlen(s));
diff --git a/debian/patches/keepalive-extensions.patch b/debian/patches/keepalive-extensions.patch
new file mode 100644
index 000000000..d8362de70
--- /dev/null
+++ b/debian/patches/keepalive-extensions.patch
@@ -0,0 +1,124 @@
1Description: Various keepalive extensions
2 Add compatibility aliases for ProtocolKeepAlives and SetupTimeOut,
3 supported in previous versions of Debian's OpenSSH package but since
4 superseded by ServerAliveInterval. (We're probably stuck with this bit for
5 compatibility.)
6 .
7 In batch mode, default ServerAliveInterval to five minutes.
8 .
9 Adjust documentation to match and to give some more advice on use of
10 keepalives.
11Author: Richard Kettlewell <rjk@greenend.org.uk>
12Author: Ian Jackson <ian@chiark.greenend.org.uk>
13Author: Matthew Vernon <matthew@debian.org>
14Author: Colin Watson <cjwatson@debian.org>
15Last-Update: 2010-02-27
16
17Index: b/readconf.c
18===================================================================
19--- a/readconf.c
20+++ b/readconf.c
21@@ -138,6 +138,7 @@
22 oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
23 oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
24 oKexAlgorithms, oIPQoS, oRequestTTY,
25+ oProtocolKeepAlives, oSetupTimeOut,
26 oDeprecated, oUnsupported
27 } OpCodes;
28
29@@ -259,6 +260,8 @@
30 { "kexalgorithms", oKexAlgorithms },
31 { "ipqos", oIPQoS },
32 { "requesttty", oRequestTTY },
33+ { "protocolkeepalives", oProtocolKeepAlives },
34+ { "setuptimeout", oSetupTimeOut },
35
36 { NULL, oBadOption }
37 };
38@@ -914,6 +917,8 @@
39 goto parse_flag;
40
41 case oServerAliveInterval:
42+ case oProtocolKeepAlives: /* Debian-specific compatibility alias */
43+ case oSetupTimeOut: /* Debian-specific compatibility alias */
44 intptr = &options->server_alive_interval;
45 goto parse_time;
46
47@@ -1385,8 +1390,13 @@
48 options->rekey_limit = 0;
49 if (options->verify_host_key_dns == -1)
50 options->verify_host_key_dns = 0;
51- if (options->server_alive_interval == -1)
52- options->server_alive_interval = 0;
53+ if (options->server_alive_interval == -1) {
54+ /* in batch mode, default is 5mins */
55+ if (options->batch_mode == 1)
56+ options->server_alive_interval = 300;
57+ else
58+ options->server_alive_interval = 0;
59+ }
60 if (options->server_alive_count_max == -1)
61 options->server_alive_count_max = 3;
62 if (options->control_master == -1)
63Index: b/ssh_config.5
64===================================================================
65--- a/ssh_config.5
66+++ b/ssh_config.5
67@@ -136,8 +136,12 @@
68 If set to
69 .Dq yes ,
70 passphrase/password querying will be disabled.
71+In addition, the
72+.Cm ServerAliveInterval
73+option will be set to 300 seconds by default.
74 This option is useful in scripts and other batch jobs where no user
75-is present to supply the password.
76+is present to supply the password,
77+and where it is desirable to detect a broken network swiftly.
78 The argument must be
79 .Dq yes
80 or
81@@ -1100,8 +1104,15 @@
82 will send a message through the encrypted
83 channel to request a response from the server.
84 The default
85-is 0, indicating that these messages will not be sent to the server.
86+is 0, indicating that these messages will not be sent to the server,
87+or 300 if the
88+.Cm BatchMode
89+option is set.
90 This option applies to protocol version 2 only.
91+.Cm ProtocolKeepAlives
92+and
93+.Cm SetupTimeOut
94+are Debian-specific compatibility aliases for this option.
95 .It Cm StrictHostKeyChecking
96 If this flag is set to
97 .Dq yes ,
98@@ -1140,6 +1151,12 @@
99 other side.
100 If they are sent, death of the connection or crash of one
101 of the machines will be properly noticed.
102+This option only uses TCP keepalives (as opposed to using ssh level
103+keepalives), so takes a long time to notice when the connection dies.
104+As such, you probably want
105+the
106+.Cm ServerAliveInterval
107+option as well.
108 However, this means that
109 connections will die if the route is down temporarily, and some people
110 find it annoying.
111Index: b/sshd_config.5
112===================================================================
113--- a/sshd_config.5
114+++ b/sshd_config.5
115@@ -1037,6 +1037,9 @@
116 .Pp
117 To disable TCP keepalive messages, the value should be set to
118 .Dq no .
119+.Pp
120+This option was formerly called
121+.Cm KeepAlive .
122 .It Cm TrustedUserCAKeys
123 Specifies a file containing public keys of certificate authorities that are
124 trusted to sign user certificates for authentication.
diff --git a/debian/patches/lintian-symlink-pickiness.patch b/debian/patches/lintian-symlink-pickiness.patch
new file mode 100644
index 000000000..7ee91cce8
--- /dev/null
+++ b/debian/patches/lintian-symlink-pickiness.patch
@@ -0,0 +1,23 @@
1Description: Fix picky lintian errors about slogin symlinks
2 Apparently this breaks some SVR4 packaging systems, so upstream can't win
3 either way and opted to keep the status quo. We need this patch anyway.
4Author: Colin Watson <cjwatson@debian.org>
5Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1728
6Last-Update: 2010-04-10
7
8Index: b/Makefile.in
9===================================================================
10--- a/Makefile.in
11+++ b/Makefile.in
12@@ -282,9 +282,9 @@
13 $(INSTALL) -m 644 ssh-pkcs11-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8
14 $(INSTALL) -m 644 ssh-vulnkey.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1
15 -rm -f $(DESTDIR)$(bindir)/slogin
16- ln -s ./ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin
17+ ln -s ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin
18 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1
19- ln -s ./ssh.1 $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1
20+ ln -s ssh.1 $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1
21
22 install-sysconf:
23 if [ ! -d $(DESTDIR)$(sysconfdir) ]; then \
diff --git a/debian/patches/openbsd-docs.patch b/debian/patches/openbsd-docs.patch
new file mode 100644
index 000000000..bda5f0c24
--- /dev/null
+++ b/debian/patches/openbsd-docs.patch
@@ -0,0 +1,135 @@
1Description: Adjust various OpenBSD-specific references in manual pages
2 No single bug reference for this patch, but history includes:
3 http://bugs.debian.org/154434 (login.conf(5))
4 http://bugs.debian.org/513417 (/etc/rc)
5 http://bugs.debian.org/530692 (ssl(8))
6 https://bugs.launchpad.net/bugs/456660 (ssl(8))
7Author: Colin Watson <cjwatson@debian.org>
8Forwarded: not-needed
9Last-Update: 2010-02-28
10
11Index: b/moduli.5
12===================================================================
13--- a/moduli.5
14+++ b/moduli.5
15@@ -21,7 +21,7 @@
16 .Nd Diffie-Hellman moduli
17 .Sh DESCRIPTION
18 The
19-.Pa /etc/moduli
20+.Pa /etc/ssh/moduli
21 file contains prime numbers and generators for use by
22 .Xr sshd 8
23 in the Diffie-Hellman Group Exchange key exchange method.
24@@ -110,7 +110,7 @@
25 Diffie-Hellman output to sufficiently key the selected symmetric cipher.
26 .Xr sshd 8
27 then randomly selects a modulus from
28-.Fa /etc/moduli
29+.Fa /etc/ssh/moduli
30 that best meets the size requirement.
31 .Sh SEE ALSO
32 .Xr ssh-keygen 1 ,
33Index: b/ssh-keygen.1
34===================================================================
35--- a/ssh-keygen.1
36+++ b/ssh-keygen.1
37@@ -149,9 +149,7 @@
38 .Pa ~/.ssh/id_dsa
39 or
40 .Pa ~/.ssh/id_rsa .
41-Additionally, the system administrator may use this to generate host keys,
42-as seen in
43-.Pa /etc/rc .
44+Additionally, the system administrator may use this to generate host keys.
45 .Pp
46 Normally this program generates the key and asks for a file in which
47 to store the private key.
48@@ -197,9 +195,7 @@
49 For each of the key types (rsa1, rsa, dsa and ecdsa) for which host keys
50 do not exist, generate the host keys with the default key file path,
51 an empty passphrase, default bits for the key type, and default comment.
52-This is used by
53-.Pa /etc/rc
54-to generate new host keys.
55+This is used by system administration scripts to generate new host keys.
56 .It Fl a Ar trials
57 Specifies the number of primality tests to perform when screening DH-GEX
58 candidates using the
59@@ -535,7 +531,7 @@
60 Valid generator values are 2, 3, and 5.
61 .Pp
62 Screened DH groups may be installed in
63-.Pa /etc/moduli .
64+.Pa /etc/ssh/moduli .
65 It is important that this file contains moduli of a range of bit lengths and
66 that both ends of a connection share common moduli.
67 .Sh CERTIFICATES
68@@ -661,7 +657,7 @@
69 where the user wishes to log in using public key authentication.
70 There is no need to keep the contents of this file secret.
71 .Pp
72-.It Pa /etc/moduli
73+.It Pa /etc/ssh/moduli
74 Contains Diffie-Hellman groups used for DH-GEX.
75 The file format is described in
76 .Xr moduli 5 .
77Index: b/ssh.1
78===================================================================
79--- a/ssh.1
80+++ b/ssh.1
81@@ -731,6 +731,10 @@
82 .Sx HISTORY
83 section of
84 .Xr ssl 8
85+(on non-OpenBSD systems, see
86+.nh
87+http://www.openbsd.org/cgi\-bin/man.cgi?query=ssl&sektion=8#HISTORY)
88+.hy
89 contains a brief discussion of the DSA and RSA algorithms.
90 .Pp
91 The file
92Index: b/sshd.8
93===================================================================
94--- a/sshd.8
95+++ b/sshd.8
96@@ -69,7 +69,7 @@
97 .Nm
98 listens for connections from clients.
99 It is normally started at boot from
100-.Pa /etc/rc .
101+.Pa /etc/init.d/ssh .
102 It forks a new
103 daemon for each incoming connection.
104 The forked daemons handle
105@@ -853,7 +853,7 @@
106 .Xr ssh 1 ) .
107 It should only be writable by root.
108 .Pp
109-.It Pa /etc/moduli
110+.It Pa /etc/ssh/moduli
111 Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange".
112 The file format is described in
113 .Xr moduli 5 .
114@@ -951,7 +951,6 @@
115 .Xr ssh-vulnkey 1 ,
116 .Xr chroot 2 ,
117 .Xr hosts_access 5 ,
118-.Xr login.conf 5 ,
119 .Xr moduli 5 ,
120 .Xr sshd_config 5 ,
121 .Xr inetd 8 ,
122Index: b/sshd_config.5
123===================================================================
124--- a/sshd_config.5
125+++ b/sshd_config.5
126@@ -222,8 +222,7 @@
127 By default, no banner is displayed.
128 .It Cm ChallengeResponseAuthentication
129 Specifies whether challenge-response authentication is allowed (e.g. via
130-PAM or though authentication styles supported in
131-.Xr login.conf 5 )
132+PAM).
133 The default is
134 .Dq yes .
135 .It Cm ChrootDirectory
diff --git a/debian/patches/package-versioning.patch b/debian/patches/package-versioning.patch
new file mode 100644
index 000000000..95444cbd4
--- /dev/null
+++ b/debian/patches/package-versioning.patch
@@ -0,0 +1,50 @@
1Description: Include the Debian version in our identification
2 This makes it easier to audit networks for versions patched against
3 security vulnerabilities. It has little detrimental effect, as attackers
4 will generally just try attacks rather than bothering to scan for
5 vulnerable-looking version strings. (However, see debian-banner.patch.)
6Author: Matthew Vernon <matthew@debian.org>
7Forwarded: not-needed
8Last-Update: 2010-02-28
9
10Index: b/sshconnect.c
11===================================================================
12--- a/sshconnect.c
13+++ b/sshconnect.c
14@@ -556,7 +556,7 @@
15 snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s",
16 compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
17 compat20 ? PROTOCOL_MINOR_2 : minor1,
18- SSH_VERSION, compat20 ? "\r\n" : "\n");
19+ SSH_RELEASE, compat20 ? "\r\n" : "\n");
20 if (roaming_atomicio(vwrite, connection_out, buf, strlen(buf))
21 != strlen(buf))
22 fatal("write: %.100s", strerror(errno));
23Index: b/sshd.c
24===================================================================
25--- a/sshd.c
26+++ b/sshd.c
27@@ -423,7 +423,7 @@
28 minor = PROTOCOL_MINOR_1;
29 }
30 snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor,
31- SSH_VERSION, newline);
32+ SSH_RELEASE, newline);
33 server_version_string = xstrdup(buf);
34
35 /* Send our protocol version identification. */
36Index: b/version.h
37===================================================================
38--- a/version.h
39+++ b/version.h
40@@ -3,4 +3,9 @@
41 #define SSH_VERSION "OpenSSH_5.9"
42
43 #define SSH_PORTABLE "p1"
44-#define SSH_RELEASE SSH_VERSION SSH_PORTABLE
45+#define SSH_RELEASE_MINIMUM SSH_VERSION SSH_PORTABLE
46+#ifdef SSH_EXTRAVERSION
47+#define SSH_RELEASE SSH_RELEASE_MINIMUM " " SSH_EXTRAVERSION
48+#else
49+#define SSH_RELEASE SSH_RELEASE_MINIMUM
50+#endif
diff --git a/debian/patches/quieter-signals.patch b/debian/patches/quieter-signals.patch
new file mode 100644
index 000000000..ff41f094d
--- /dev/null
+++ b/debian/patches/quieter-signals.patch
@@ -0,0 +1,31 @@
1Description: Reduce severity of "Killed by signal %d"
2 This produces irritating messages when using ProxyCommand or other programs
3 that use ssh under the covers (e.g. Subversion). These messages are more
4 normally printed by the calling program, such as the shell.
5 .
6 According to the upstream bug, the right way to avoid this is to use the -q
7 option, so we may drop this patch after further investigation into whether
8 any software in Debian is still relying on it.
9Author: Peter Samuelson <peter@p12n.org>
10Author: Colin Watson <cjwatson@debian.org>
11Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1118
12Bug-Debian: http://bugs.debian.org/313371
13Last-Update: 2010-02-27
14
15Index: b/clientloop.c
16===================================================================
17--- a/clientloop.c
18+++ b/clientloop.c
19@@ -1619,8 +1619,10 @@
20 exit_status = 0;
21 }
22
23- if (received_signal)
24- fatal("Killed by signal %d.", (int) received_signal);
25+ if (received_signal) {
26+ debug("Killed by signal %d.", (int) received_signal);
27+ cleanup_exit((int) received_signal + 128);
28+ }
29
30 /*
31 * In interactive mode (with pseudo tty) display a message indicating
diff --git a/debian/patches/scp-quoting.patch b/debian/patches/scp-quoting.patch
new file mode 100644
index 000000000..239c1b599
--- /dev/null
+++ b/debian/patches/scp-quoting.patch
@@ -0,0 +1,32 @@
1Description: Adjust scp quoting in verbose mode
2 Tweak scp's reporting of filenames in verbose mode to be a bit less
3 confusing with spaces.
4 .
5 This should be revised to mimic real shell quoting.
6Author: Nicolas Valcárcel <nvalcarcel@ubuntu.com>
7Bug-Ubuntu: https://bugs.launchpad.net/bugs/89945
8Last-Update: 2010-02-27
9
10Index: b/scp.c
11===================================================================
12--- a/scp.c
13+++ b/scp.c
14@@ -189,8 +189,16 @@
15
16 if (verbose_mode) {
17 fprintf(stderr, "Executing:");
18- for (i = 0; i < a->num; i++)
19- fprintf(stderr, " %s", a->list[i]);
20+ for (i = 0; i < a->num; i++) {
21+ if (i == 0)
22+ fprintf(stderr, " %s", a->list[i]);
23+ else
24+ /*
25+ * TODO: misbehaves if a->list[i] contains a
26+ * single quote
27+ */
28+ fprintf(stderr, " '%s'", a->list[i]);
29+ }
30 fprintf(stderr, "\n");
31 }
32 if ((pid = fork()) == -1)
diff --git a/debian/patches/selinux-role.patch b/debian/patches/selinux-role.patch
new file mode 100644
index 000000000..b14402199
--- /dev/null
+++ b/debian/patches/selinux-role.patch
@@ -0,0 +1,482 @@
1Description: Handle SELinux authorisation roles
2 Rejected upstream due to discomfort with magic usernames; a better approach
3 will need an SSH protocol change. In the meantime, this came from Debian's
4 SELinux maintainer, so we'll keep it until we have something better.
5Author: Manoj Srivastava <srivasta@debian.org>
6Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1641
7Bug-Debian: http://bugs.debian.org/394795
8Last-Update: 2010-02-27
9
10Index: b/auth.h
11===================================================================
12--- a/auth.h
13+++ b/auth.h
14@@ -59,6 +59,7 @@
15 char *service;
16 struct passwd *pw; /* set if 'valid' */
17 char *style;
18+ char *role;
19 void *kbdintctxt;
20 void *jpake_ctx;
21 #ifdef BSD_AUTH
22Index: b/auth1.c
23===================================================================
24--- a/auth1.c
25+++ b/auth1.c
26@@ -383,7 +383,7 @@
27 do_authentication(Authctxt *authctxt)
28 {
29 u_int ulen;
30- char *user, *style = NULL;
31+ char *user, *style = NULL, *role = NULL;
32
33 /* Get the name of the user that we wish to log in as. */
34 packet_read_expect(SSH_CMSG_USER);
35@@ -392,11 +392,17 @@
36 user = packet_get_cstring(&ulen);
37 packet_check_eom();
38
39+ if ((role = strchr(user, '/')) != NULL)
40+ *role++ = '\0';
41+
42 if ((style = strchr(user, ':')) != NULL)
43 *style++ = '\0';
44+ else if (role && (style = strchr(role, ':')) != NULL)
45+ *style++ = '\0';
46
47 authctxt->user = user;
48 authctxt->style = style;
49+ authctxt->role = role;
50
51 /* Verify that the user is a valid user. */
52 if ((authctxt->pw = PRIVSEP(getpwnamallow(user))) != NULL)
53Index: b/auth2.c
54===================================================================
55--- a/auth2.c
56+++ b/auth2.c
57@@ -217,7 +217,7 @@
58 {
59 Authctxt *authctxt = ctxt;
60 Authmethod *m = NULL;
61- char *user, *service, *method, *style = NULL;
62+ char *user, *service, *method, *style = NULL, *role = NULL;
63 int authenticated = 0;
64
65 if (authctxt == NULL)
66@@ -229,8 +229,13 @@
67 debug("userauth-request for user %s service %s method %s", user, service, method);
68 debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
69
70+ if ((role = strchr(user, '/')) != NULL)
71+ *role++ = 0;
72+
73 if ((style = strchr(user, ':')) != NULL)
74 *style++ = 0;
75+ else if (role && (style = strchr(role, ':')) != NULL)
76+ *style++ = '\0';
77
78 if (authctxt->attempt++ == 0) {
79 /* setup auth context */
80@@ -254,8 +259,9 @@
81 use_privsep ? " [net]" : "");
82 authctxt->service = xstrdup(service);
83 authctxt->style = style ? xstrdup(style) : NULL;
84+ authctxt->role = role ? xstrdup(role) : NULL;
85 if (use_privsep)
86- mm_inform_authserv(service, style);
87+ mm_inform_authserv(service, style, role);
88 userauth_banner();
89 } else if (strcmp(user, authctxt->user) != 0 ||
90 strcmp(service, authctxt->service) != 0) {
91Index: b/monitor.c
92===================================================================
93--- a/monitor.c
94+++ b/monitor.c
95@@ -145,6 +145,7 @@
96 int mm_answer_pwnamallow(int, Buffer *);
97 int mm_answer_auth2_read_banner(int, Buffer *);
98 int mm_answer_authserv(int, Buffer *);
99+int mm_answer_authrole(int, Buffer *);
100 int mm_answer_authpassword(int, Buffer *);
101 int mm_answer_bsdauthquery(int, Buffer *);
102 int mm_answer_bsdauthrespond(int, Buffer *);
103@@ -225,6 +226,7 @@
104 {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
105 {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
106 {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
107+ {MONITOR_REQ_AUTHROLE, MON_ONCE, mm_answer_authrole},
108 {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
109 {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
110 #ifdef USE_PAM
111@@ -810,6 +812,7 @@
112 else {
113 /* Allow service/style information on the auth context */
114 monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1);
115+ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHROLE, 1);
116 monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1);
117 }
118 #ifdef USE_PAM
119@@ -842,14 +845,37 @@
120
121 authctxt->service = buffer_get_string(m, NULL);
122 authctxt->style = buffer_get_string(m, NULL);
123- debug3("%s: service=%s, style=%s",
124- __func__, authctxt->service, authctxt->style);
125+ authctxt->role = buffer_get_string(m, NULL);
126+ debug3("%s: service=%s, style=%s, role=%s",
127+ __func__, authctxt->service, authctxt->style, authctxt->role);
128
129 if (strlen(authctxt->style) == 0) {
130 xfree(authctxt->style);
131 authctxt->style = NULL;
132 }
133
134+ if (strlen(authctxt->role) == 0) {
135+ xfree(authctxt->role);
136+ authctxt->role = NULL;
137+ }
138+
139+ return (0);
140+}
141+
142+int
143+mm_answer_authrole(int sock, Buffer *m)
144+{
145+ monitor_permit_authentications(1);
146+
147+ authctxt->role = buffer_get_string(m, NULL);
148+ debug3("%s: role=%s",
149+ __func__, authctxt->role);
150+
151+ if (strlen(authctxt->role) == 0) {
152+ xfree(authctxt->role);
153+ authctxt->role = NULL;
154+ }
155+
156 return (0);
157 }
158
159@@ -1437,7 +1463,7 @@
160 res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty));
161 if (res == 0)
162 goto error;
163- pty_setowner(authctxt->pw, s->tty);
164+ pty_setowner(authctxt->pw, s->tty, authctxt->role);
165
166 buffer_put_int(m, 1);
167 buffer_put_cstring(m, s->tty);
168Index: b/monitor.h
169===================================================================
170--- a/monitor.h
171+++ b/monitor.h
172@@ -30,7 +30,7 @@
173
174 enum monitor_reqtype {
175 MONITOR_REQ_MODULI, MONITOR_ANS_MODULI,
176- MONITOR_REQ_FREE, MONITOR_REQ_AUTHSERV,
177+ MONITOR_REQ_FREE, MONITOR_REQ_AUTHSERV, MONITOR_REQ_AUTHROLE,
178 MONITOR_REQ_SIGN, MONITOR_ANS_SIGN,
179 MONITOR_REQ_PWNAM, MONITOR_ANS_PWNAM,
180 MONITOR_REQ_AUTH2_READ_BANNER, MONITOR_ANS_AUTH2_READ_BANNER,
181Index: b/monitor_wrap.c
182===================================================================
183--- a/monitor_wrap.c
184+++ b/monitor_wrap.c
185@@ -318,10 +318,10 @@
186 return (banner);
187 }
188
189-/* Inform the privileged process about service and style */
190+/* Inform the privileged process about service, style, and role */
191
192 void
193-mm_inform_authserv(char *service, char *style)
194+mm_inform_authserv(char *service, char *style, char *role)
195 {
196 Buffer m;
197
198@@ -330,11 +330,29 @@
199 buffer_init(&m);
200 buffer_put_cstring(&m, service);
201 buffer_put_cstring(&m, style ? style : "");
202+ buffer_put_cstring(&m, role ? role : "");
203
204 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHSERV, &m);
205
206 buffer_free(&m);
207 }
208+
209+/* Inform the privileged process about role */
210+
211+void
212+mm_inform_authrole(char *role)
213+{
214+ Buffer m;
215+
216+ debug3("%s entering", __func__);
217+
218+ buffer_init(&m);
219+ buffer_put_cstring(&m, role ? role : "");
220+
221+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHROLE, &m);
222+
223+ buffer_free(&m);
224+}
225
226 /* Do the password authentication */
227 int
228Index: b/monitor_wrap.h
229===================================================================
230--- a/monitor_wrap.h
231+++ b/monitor_wrap.h
232@@ -41,7 +41,8 @@
233 int mm_is_monitor(void);
234 DH *mm_choose_dh(int, int, int);
235 int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int);
236-void mm_inform_authserv(char *, char *);
237+void mm_inform_authserv(char *, char *, char *);
238+void mm_inform_authrole(char *);
239 struct passwd *mm_getpwnamallow(const char *);
240 char *mm_auth2_read_banner(void);
241 int mm_auth_password(struct Authctxt *, char *);
242Index: b/openbsd-compat/port-linux.c
243===================================================================
244--- a/openbsd-compat/port-linux.c
245+++ b/openbsd-compat/port-linux.c
246@@ -29,6 +29,12 @@
247 #include <string.h>
248 #include <stdio.h>
249
250+#ifdef WITH_SELINUX
251+#include "key.h"
252+#include "hostfile.h"
253+#include "auth.h"
254+#endif
255+
256 #include "log.h"
257 #include "xmalloc.h"
258 #include "port-linux.h"
259@@ -58,9 +64,9 @@
260
261 /* Return the default security context for the given username */
262 static security_context_t
263-ssh_selinux_getctxbyname(char *pwname)
264+ssh_selinux_getctxbyname(char *pwname, const char *role)
265 {
266- security_context_t sc;
267+ security_context_t sc = NULL;
268 char *sename = NULL, *lvl = NULL;
269 int r;
270
271@@ -73,9 +79,16 @@
272 #endif
273
274 #ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL
275- r = get_default_context_with_level(sename, lvl, NULL, &sc);
276+ if (role != NULL && role[0])
277+ r = get_default_context_with_rolelevel(sename, role, lvl, NULL,
278+ &sc);
279+ else
280+ r = get_default_context_with_level(sename, lvl, NULL, &sc);
281 #else
282- r = get_default_context(sename, NULL, &sc);
283+ if (role != NULL && role[0])
284+ r = get_default_context_with_role(sename, role, NULL, &sc);
285+ else
286+ r = get_default_context(sename, NULL, &sc);
287 #endif
288
289 if (r != 0) {
290@@ -106,7 +119,7 @@
291
292 /* Set the execution context to the default for the specified user */
293 void
294-ssh_selinux_setup_exec_context(char *pwname)
295+ssh_selinux_setup_exec_context(char *pwname, const char *role)
296 {
297 security_context_t user_ctx = NULL;
298
299@@ -115,7 +128,7 @@
300
301 debug3("%s: setting execution context", __func__);
302
303- user_ctx = ssh_selinux_getctxbyname(pwname);
304+ user_ctx = ssh_selinux_getctxbyname(pwname, role);
305 if (setexeccon(user_ctx) != 0) {
306 switch (security_getenforce()) {
307 case -1:
308@@ -137,7 +150,7 @@
309
310 /* Set the TTY context for the specified user */
311 void
312-ssh_selinux_setup_pty(char *pwname, const char *tty)
313+ssh_selinux_setup_pty(char *pwname, const char *tty, const char *role)
314 {
315 security_context_t new_tty_ctx = NULL;
316 security_context_t user_ctx = NULL;
317@@ -148,7 +161,7 @@
318
319 debug3("%s: setting TTY context on %s", __func__, tty);
320
321- user_ctx = ssh_selinux_getctxbyname(pwname);
322+ user_ctx = ssh_selinux_getctxbyname(pwname, role);
323
324 /* XXX: should these calls fatal() upon failure in enforcing mode? */
325
326Index: b/openbsd-compat/port-linux.h
327===================================================================
328--- a/openbsd-compat/port-linux.h
329+++ b/openbsd-compat/port-linux.h
330@@ -21,8 +21,8 @@
331
332 #ifdef WITH_SELINUX
333 int ssh_selinux_enabled(void);
334-void ssh_selinux_setup_pty(char *, const char *);
335-void ssh_selinux_setup_exec_context(char *);
336+void ssh_selinux_setup_pty(char *, const char *, const char *);
337+void ssh_selinux_setup_exec_context(char *, const char *);
338 void ssh_selinux_change_context(const char *);
339 void ssh_selinux_setfscreatecon(const char *);
340 #endif
341Index: b/platform.c
342===================================================================
343--- a/platform.c
344+++ b/platform.c
345@@ -134,7 +134,7 @@
346 * called if sshd is running as root.
347 */
348 void
349-platform_setusercontext_post_groups(struct passwd *pw)
350+platform_setusercontext_post_groups(struct passwd *pw, const char *role)
351 {
352 #if !defined(HAVE_LOGIN_CAP) && defined(USE_PAM)
353 /*
354@@ -181,7 +181,7 @@
355 }
356 #endif /* HAVE_SETPCRED */
357 #ifdef WITH_SELINUX
358- ssh_selinux_setup_exec_context(pw->pw_name);
359+ ssh_selinux_setup_exec_context(pw->pw_name, role);
360 #endif
361 }
362
363Index: b/platform.h
364===================================================================
365--- a/platform.h
366+++ b/platform.h
367@@ -26,7 +26,7 @@
368 void platform_post_fork_child(void);
369 int platform_privileged_uidswap(void);
370 void platform_setusercontext(struct passwd *);
371-void platform_setusercontext_post_groups(struct passwd *);
372+void platform_setusercontext_post_groups(struct passwd *, const char *);
373 char *platform_get_krb5_client(const char *);
374 char *platform_krb5_get_principal_name(const char *);
375
376Index: b/session.c
377===================================================================
378--- a/session.c
379+++ b/session.c
380@@ -1471,7 +1471,7 @@
381
382 /* Set login name, uid, gid, and groups. */
383 void
384-do_setusercontext(struct passwd *pw)
385+do_setusercontext(struct passwd *pw, const char *role)
386 {
387 char *chroot_path, *tmp;
388
389@@ -1499,7 +1499,7 @@
390 endgrent();
391 #endif
392
393- platform_setusercontext_post_groups(pw);
394+ platform_setusercontext_post_groups(pw, role);
395
396 if (options.chroot_directory != NULL &&
397 strcasecmp(options.chroot_directory, "none") != 0) {
398@@ -1625,7 +1625,7 @@
399
400 /* Force a password change */
401 if (s->authctxt->force_pwchange) {
402- do_setusercontext(pw);
403+ do_setusercontext(pw, s->authctxt->role);
404 child_close_fds();
405 do_pwchange(s);
406 exit(1);
407@@ -1652,7 +1652,7 @@
408 /* When PAM is enabled we rely on it to do the nologin check */
409 if (!options.use_pam)
410 do_nologin(pw);
411- do_setusercontext(pw);
412+ do_setusercontext(pw, s->authctxt->role);
413 /*
414 * PAM session modules in do_setusercontext may have
415 * generated messages, so if this in an interactive
416@@ -2064,7 +2064,7 @@
417 tty_parse_modes(s->ttyfd, &n_bytes);
418
419 if (!use_privsep)
420- pty_setowner(s->pw, s->tty);
421+ pty_setowner(s->pw, s->tty, s->authctxt->role);
422
423 /* Set window size from the packet. */
424 pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
425Index: b/session.h
426===================================================================
427--- a/session.h
428+++ b/session.h
429@@ -76,7 +76,7 @@
430 Session *session_new(void);
431 Session *session_by_tty(char *);
432 void session_close(Session *);
433-void do_setusercontext(struct passwd *);
434+void do_setusercontext(struct passwd *, const char *);
435 void child_set_env(char ***envp, u_int *envsizep, const char *name,
436 const char *value);
437
438Index: b/sshd.c
439===================================================================
440--- a/sshd.c
441+++ b/sshd.c
442@@ -730,7 +730,7 @@
443 RAND_seed(rnd, sizeof(rnd));
444
445 /* Drop privileges */
446- do_setusercontext(authctxt->pw);
447+ do_setusercontext(authctxt->pw, authctxt->role);
448
449 skip:
450 /* It is safe now to apply the key state */
451Index: b/sshpty.c
452===================================================================
453--- a/sshpty.c
454+++ b/sshpty.c
455@@ -200,7 +200,7 @@
456 }
457
458 void
459-pty_setowner(struct passwd *pw, const char *tty)
460+pty_setowner(struct passwd *pw, const char *tty, const char *role)
461 {
462 struct group *grp;
463 gid_t gid;
464@@ -227,7 +227,7 @@
465 strerror(errno));
466
467 #ifdef WITH_SELINUX
468- ssh_selinux_setup_pty(pw->pw_name, tty);
469+ ssh_selinux_setup_pty(pw->pw_name, tty, role);
470 #endif
471
472 if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
473Index: b/sshpty.h
474===================================================================
475--- a/sshpty.h
476+++ b/sshpty.h
477@@ -24,4 +24,4 @@
478 void pty_release(const char *);
479 void pty_make_controlling_tty(int *, const char *);
480 void pty_change_window_size(int, u_int, u_int, u_int, u_int);
481-void pty_setowner(struct passwd *, const char *);
482+void pty_setowner(struct passwd *, const char *, const char *);
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 000000000..2be7cf10a
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,43 @@
1# GSSAPI
2gssapi.patch
3
4# SELinux
5selinux-role.patch
6
7# Key blacklisting
8ssh-vulnkey.patch
9
10# Keepalive handling
11ssh1-keepalive.patch
12keepalive-extensions.patch
13
14# Message adjustments
15syslog-level-silent.patch
16quieter-signals.patch
17helpful-wait-terminate.patch
18
19# Miscellaneous bug fixes
20user-group-modes.patch
21scp-quoting.patch
22shell-path.patch
23dnssec-sshfp.patch
24
25# Versioning
26package-versioning.patch
27debian-banner.patch
28
29# File system layout
30authorized-keys-man-symlink.patch
31lintian-symlink-pickiness.patch
32
33# Documentation
34openbsd-docs.patch
35ssh-argv0.patch
36doc-hash-tab-completion.patch
37
38# Miscellaneous bug fixes
39auth-log-verbosity.patch
40
41# Debian-specific configuration
42gnome-ssh-askpass2-icon.patch
43debian-config.patch
diff --git a/debian/patches/shell-path.patch b/debian/patches/shell-path.patch
new file mode 100644
index 000000000..8c549128b
--- /dev/null
+++ b/debian/patches/shell-path.patch
@@ -0,0 +1,30 @@
1Description: Look for $SHELL on the path for ProxyCommand/LocalCommand
2 There's some debate on the upstream bug about whether POSIX requires this.
3 I (Colin Watson) agree with Vincent and think it does.
4Author: Colin Watson <cjwatson@debian.org>
5Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1494
6Bug-Debian: http://bugs.debian.org/492728
7Last-Update: 2010-02-27
8
9Index: b/sshconnect.c
10===================================================================
11--- a/sshconnect.c
12+++ b/sshconnect.c
13@@ -144,7 +144,7 @@
14 /* Execute the proxy command. Note that we gave up any
15 extra privileges above. */
16 signal(SIGPIPE, SIG_DFL);
17- execv(argv[0], argv);
18+ execvp(argv[0], argv);
19 perror(argv[0]);
20 exit(1);
21 }
22@@ -1273,7 +1273,7 @@
23 if (pid == 0) {
24 signal(SIGPIPE, SIG_DFL);
25 debug3("Executing %s -c \"%s\"", shell, args);
26- execl(shell, shell, "-c", args, (char *)NULL);
27+ execlp(shell, shell, "-c", args, (char *)NULL);
28 error("Couldn't execute %s -c \"%s\": %s",
29 shell, args, strerror(errno));
30 _exit(1);
diff --git a/debian/patches/ssh-argv0.patch b/debian/patches/ssh-argv0.patch
new file mode 100644
index 000000000..a7750ed23
--- /dev/null
+++ b/debian/patches/ssh-argv0.patch
@@ -0,0 +1,21 @@
1Description: ssh(1): Refer to ssh-argv0(1)
2 Old versions of OpenSSH (up to 2.5 or thereabouts) allowed creating
3 symlinks to ssh with the name of the host you want to connect to. Debian
4 ships an ssh-argv0 script restoring this feature; this patch refers to its
5 manual page from ssh(1).
6Bug-Debian: http://bugs.debian.org/111341
7Forwarded: not-needed
8Last-Update: 2010-02-28
9
10Index: b/ssh.1
11===================================================================
12--- a/ssh.1
13+++ b/ssh.1
14@@ -1411,6 +1411,7 @@
15 .Xr sftp 1 ,
16 .Xr ssh-add 1 ,
17 .Xr ssh-agent 1 ,
18+.Xr ssh-argv0 1 ,
19 .Xr ssh-keygen 1 ,
20 .Xr ssh-keyscan 1 ,
21 .Xr ssh-vulnkey 1 ,
diff --git a/debian/patches/ssh-vulnkey.patch b/debian/patches/ssh-vulnkey.patch
new file mode 100644
index 000000000..4245319c3
--- /dev/null
+++ b/debian/patches/ssh-vulnkey.patch
@@ -0,0 +1,1382 @@
1Description: Reject vulnerable keys to mitigate Debian OpenSSL flaw
2 In 2008, Debian (and derived distributions such as Ubuntu) shipped an
3 OpenSSL package with a flawed random number generator, causing OpenSSH to
4 generate only a very limited set of keys which were subject to private half
5 precomputation. To mitigate this, this patch checks key authentications
6 against a blacklist of known-vulnerable keys, and adds a new ssh-vulnkey
7 program which can be used to explicitly check keys against that blacklist.
8 See CVE-2008-0166.
9Author: Colin Watson <cjwatson@ubuntu.com>
10Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1469
11Last-Update: 2010-02-27
12
13Index: b/Makefile.in
14===================================================================
15--- a/Makefile.in
16+++ b/Makefile.in
17@@ -26,6 +26,7 @@
18 SFTP_SERVER=$(libexecdir)/sftp-server
19 SSH_KEYSIGN=$(libexecdir)/ssh-keysign
20 SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper
21+SSH_DATADIR=$(datadir)/ssh
22 PRIVSEP_PATH=@PRIVSEP_PATH@
23 SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@
24 STRIP_OPT=@STRIP_OPT@
25@@ -38,6 +39,7 @@
26 -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \
27 -D_PATH_SSH_PIDDIR=\"$(piddir)\" \
28 -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" \
29+ -D_PATH_SSH_DATADIR=\"$(SSH_DATADIR)\" \
30
31 CC=@CC@
32 LD=@LD@
33@@ -59,7 +61,7 @@
34 EXEEXT=@EXEEXT@
35 MANFMT=@MANFMT@
36
37-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT)
38+TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-vulnkey$(EXEEXT)
39
40 LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
41 canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \
42@@ -93,8 +95,8 @@
43 roaming_common.o roaming_serv.o \
44 sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o
45
46-MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out
47-MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5
48+MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out ssh-vulnkey.1.out sshd_config.5.out ssh_config.5.out
49+MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 ssh-vulnkey.1 sshd_config.5 ssh_config.5
50 MANTYPE = @MANTYPE@
51
52 CONFIGFILES=sshd_config.out ssh_config.out moduli.out
53@@ -171,6 +173,9 @@
54 sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-common.o sftp-glob.o progressmeter.o
55 $(LD) -o $@ progressmeter.o sftp.o sftp-client.o sftp-common.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT)
56
57+ssh-vulnkey$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-vulnkey.o
58+ $(LD) -o $@ ssh-vulnkey.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
59+
60 # test driver for the loginrec code - not built by default
61 logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o
62 $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS)
63@@ -259,6 +264,7 @@
64 $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT)
65 $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT)
66 $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
67+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-vulnkey$(EXEEXT) $(DESTDIR)$(bindir)/ssh-vulnkey$(EXEEXT)
68 $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
69 $(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
70 $(INSTALL) -m 644 ssh-add.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1
71@@ -273,6 +279,7 @@
72 $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8
73 $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8
74 $(INSTALL) -m 644 ssh-pkcs11-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8
75+ $(INSTALL) -m 644 ssh-vulnkey.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1
76 -rm -f $(DESTDIR)$(bindir)/slogin
77 ln -s ./ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin
78 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1
79@@ -354,6 +361,7 @@
80 -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT)
81 -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT)
82 -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT)
83+ -rm -f $(DESTDIR)$(bindir)/ssh-vulnkey$(EXEEXT)
84 -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT)
85 -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
86 -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
87@@ -366,6 +374,7 @@
88 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1
89 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1
90 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1
91+ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-vulnkey.1
92 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8
93 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8
94 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8
95Index: b/auth-rh-rsa.c
96===================================================================
97--- a/auth-rh-rsa.c
98+++ b/auth-rh-rsa.c
99@@ -44,7 +44,7 @@
100 {
101 HostStatus host_status;
102
103- if (auth_key_is_revoked(client_host_key))
104+ if (auth_key_is_revoked(client_host_key, 0))
105 return 0;
106
107 /* Check if we would accept it using rhosts authentication. */
108Index: b/auth-rsa.c
109===================================================================
110--- a/auth-rsa.c
111+++ b/auth-rsa.c
112@@ -233,7 +233,7 @@
113 file, linenum, BN_num_bits(key->rsa->n), bits);
114
115 /* Never accept a revoked key */
116- if (auth_key_is_revoked(key))
117+ if (auth_key_is_revoked(key, 0))
118 break;
119
120 /* We have found the desired key. */
121Index: b/auth.c
122===================================================================
123--- a/auth.c
124+++ b/auth.c
125@@ -59,6 +59,7 @@
126 #include "servconf.h"
127 #include "key.h"
128 #include "hostfile.h"
129+#include "authfile.h"
130 #include "auth.h"
131 #include "auth-options.h"
132 #include "canohost.h"
133@@ -606,10 +607,34 @@
134
135 /* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */
136 int
137-auth_key_is_revoked(Key *key)
138+auth_key_is_revoked(Key *key, int hostkey)
139 {
140 char *key_fp;
141
142+ if (blacklisted_key(key, &key_fp) == 1) {
143+ if (options.permit_blacklisted_keys) {
144+ if (hostkey)
145+ error("Host key %s blacklisted (see "
146+ "ssh-vulnkey(1)); continuing anyway",
147+ key_fp);
148+ else
149+ logit("Public key %s from %s blacklisted (see "
150+ "ssh-vulnkey(1)); continuing anyway",
151+ key_fp, get_remote_ipaddr());
152+ xfree(key_fp);
153+ } else {
154+ if (hostkey)
155+ error("Host key %s blacklisted (see "
156+ "ssh-vulnkey(1))", key_fp);
157+ else
158+ logit("Public key %s from %s blacklisted (see "
159+ "ssh-vulnkey(1))",
160+ key_fp, get_remote_ipaddr());
161+ xfree(key_fp);
162+ return 1;
163+ }
164+ }
165+
166 if (options.revoked_keys_file == NULL)
167 return 0;
168
169Index: b/auth.h
170===================================================================
171--- a/auth.h
172+++ b/auth.h
173@@ -174,7 +174,7 @@
174
175 FILE *auth_openkeyfile(const char *, struct passwd *, int);
176 FILE *auth_openprincipals(const char *, struct passwd *, int);
177-int auth_key_is_revoked(Key *);
178+int auth_key_is_revoked(Key *, int);
179
180 HostStatus
181 check_key_in_hostfiles(struct passwd *, Key *, const char *,
182Index: b/auth2-hostbased.c
183===================================================================
184--- a/auth2-hostbased.c
185+++ b/auth2-hostbased.c
186@@ -146,7 +146,7 @@
187 int len;
188 char *fp;
189
190- if (auth_key_is_revoked(key))
191+ if (auth_key_is_revoked(key, 0))
192 return 0;
193
194 resolvedname = get_canonical_hostname(options.use_dns);
195Index: b/auth2-pubkey.c
196===================================================================
197--- a/auth2-pubkey.c
198+++ b/auth2-pubkey.c
199@@ -439,9 +439,10 @@
200 u_int success, i;
201 char *file;
202
203- if (auth_key_is_revoked(key))
204+ if (auth_key_is_revoked(key, 0))
205 return 0;
206- if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
207+ if (key_is_cert(key) &&
208+ auth_key_is_revoked(key->cert->signature_key, 0))
209 return 0;
210
211 success = user_cert_trusted_ca(pw, key);
212Index: b/authfile.c
213===================================================================
214--- a/authfile.c
215+++ b/authfile.c
216@@ -68,6 +68,7 @@
217 #include "rsa.h"
218 #include "misc.h"
219 #include "atomicio.h"
220+#include "pathnames.h"
221
222 #define MAX_KEY_FILE_SIZE (1024 * 1024)
223
224@@ -944,3 +945,140 @@
225 return ret;
226 }
227
228+/* Scan a blacklist of known-vulnerable keys in blacklist_file. */
229+static int
230+blacklisted_key_in_file(Key *key, const char *blacklist_file, char **fp)
231+{
232+ int fd = -1;
233+ char *dgst_hex = NULL;
234+ char *dgst_packed = NULL, *p;
235+ int i;
236+ size_t line_len;
237+ struct stat st;
238+ char buf[256];
239+ off_t start, lower, upper;
240+ int ret = 0;
241+
242+ debug("Checking blacklist file %s", blacklist_file);
243+ fd = open(blacklist_file, O_RDONLY);
244+ if (fd < 0) {
245+ ret = -1;
246+ goto out;
247+ }
248+
249+ dgst_hex = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
250+ /* Remove all colons */
251+ dgst_packed = xcalloc(1, strlen(dgst_hex) + 1);
252+ for (i = 0, p = dgst_packed; dgst_hex[i]; i++)
253+ if (dgst_hex[i] != ':')
254+ *p++ = dgst_hex[i];
255+ /* Only compare least-significant 80 bits (to keep the blacklist
256+ * size down)
257+ */
258+ line_len = strlen(dgst_packed + 12);
259+ if (line_len > 32)
260+ goto out;
261+
262+ /* Skip leading comments */
263+ start = 0;
264+ for (;;) {
265+ ssize_t r;
266+ char *newline;
267+
268+ r = atomicio(read, fd, buf, sizeof(buf));
269+ if (r <= 0)
270+ goto out;
271+ if (buf[0] != '#')
272+ break;
273+
274+ newline = memchr(buf, '\n', sizeof(buf));
275+ if (!newline)
276+ goto out;
277+ start += newline + 1 - buf;
278+ if (lseek(fd, start, SEEK_SET) < 0)
279+ goto out;
280+ }
281+
282+ /* Initialise binary search record numbers */
283+ if (fstat(fd, &st) < 0)
284+ goto out;
285+ lower = 0;
286+ upper = (st.st_size - start) / (line_len + 1);
287+
288+ while (lower != upper) {
289+ off_t cur;
290+ int cmp;
291+
292+ cur = lower + (upper - lower) / 2;
293+
294+ /* Read this line and compare to digest; this is
295+ * overflow-safe since cur < max(off_t) / (line_len + 1) */
296+ if (lseek(fd, start + cur * (line_len + 1), SEEK_SET) < 0)
297+ break;
298+ if (atomicio(read, fd, buf, line_len) != line_len)
299+ break;
300+ cmp = memcmp(buf, dgst_packed + 12, line_len);
301+ if (cmp < 0) {
302+ if (cur == lower)
303+ break;
304+ lower = cur;
305+ } else if (cmp > 0) {
306+ if (cur == upper)
307+ break;
308+ upper = cur;
309+ } else {
310+ debug("Found %s in blacklist", dgst_hex);
311+ ret = 1;
312+ break;
313+ }
314+ }
315+
316+out:
317+ if (dgst_packed)
318+ xfree(dgst_packed);
319+ if (ret != 1 && dgst_hex) {
320+ xfree(dgst_hex);
321+ dgst_hex = NULL;
322+ }
323+ if (fp)
324+ *fp = dgst_hex;
325+ if (fd >= 0)
326+ close(fd);
327+ return ret;
328+}
329+
330+/*
331+ * Scan blacklists of known-vulnerable keys. If a vulnerable key is found,
332+ * its fingerprint is returned in *fp, unless fp is NULL.
333+ */
334+int
335+blacklisted_key(Key *key, char **fp)
336+{
337+ Key *public;
338+ char *blacklist_file;
339+ int ret, ret2;
340+
341+ public = key_demote(key);
342+ if (public->type == KEY_RSA1)
343+ public->type = KEY_RSA;
344+
345+ xasprintf(&blacklist_file, "%s.%s-%u",
346+ _PATH_BLACKLIST, key_type(public), key_size(public));
347+ ret = blacklisted_key_in_file(public, blacklist_file, fp);
348+ xfree(blacklist_file);
349+ if (ret > 0) {
350+ key_free(public);
351+ return ret;
352+ }
353+
354+ xasprintf(&blacklist_file, "%s.%s-%u",
355+ _PATH_BLACKLIST_CONFIG, key_type(public), key_size(public));
356+ ret2 = blacklisted_key_in_file(public, blacklist_file, fp);
357+ xfree(blacklist_file);
358+ if (ret2 > ret)
359+ ret = ret2;
360+
361+ key_free(public);
362+ return ret;
363+}
364+
365Index: b/authfile.h
366===================================================================
367--- a/authfile.h
368+++ b/authfile.h
369@@ -28,4 +28,6 @@
370 int key_perm_ok(int, const char *);
371 int key_in_file(Key *, const char *, int);
372
373+int blacklisted_key(Key *key, char **fp);
374+
375 #endif
376Index: b/pathnames.h
377===================================================================
378--- a/pathnames.h
379+++ b/pathnames.h
380@@ -18,6 +18,10 @@
381 #define SSHDIR ETCDIR "/ssh"
382 #endif
383
384+#ifndef _PATH_SSH_DATADIR
385+#define _PATH_SSH_DATADIR "/usr/share/ssh"
386+#endif
387+
388 #ifndef _PATH_SSH_PIDDIR
389 #define _PATH_SSH_PIDDIR "/var/run"
390 #endif
391@@ -44,6 +48,9 @@
392 /* Backwards compatibility */
393 #define _PATH_DH_PRIMES SSHDIR "/primes"
394
395+#define _PATH_BLACKLIST _PATH_SSH_DATADIR "/blacklist"
396+#define _PATH_BLACKLIST_CONFIG SSHDIR "/blacklist"
397+
398 #ifndef _PATH_SSH_PROGRAM
399 #define _PATH_SSH_PROGRAM "/usr/bin/ssh"
400 #endif
401Index: b/readconf.c
402===================================================================
403--- a/readconf.c
404+++ b/readconf.c
405@@ -125,6 +125,7 @@
406 oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
407 oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
408 oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
409+ oUseBlacklistedKeys,
410 oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
411 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
412 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
413@@ -158,6 +159,7 @@
414 { "passwordauthentication", oPasswordAuthentication },
415 { "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
416 { "kbdinteractivedevices", oKbdInteractiveDevices },
417+ { "useblacklistedkeys", oUseBlacklistedKeys },
418 { "rsaauthentication", oRSAAuthentication },
419 { "pubkeyauthentication", oPubkeyAuthentication },
420 { "dsaauthentication", oPubkeyAuthentication }, /* alias */
421@@ -489,6 +491,10 @@
422 intptr = &options->challenge_response_authentication;
423 goto parse_flag;
424
425+ case oUseBlacklistedKeys:
426+ intptr = &options->use_blacklisted_keys;
427+ goto parse_flag;
428+
429 case oGssAuthentication:
430 intptr = &options->gss_authentication;
431 goto parse_flag;
432@@ -1180,6 +1186,7 @@
433 options->kbd_interactive_devices = NULL;
434 options->rhosts_rsa_authentication = -1;
435 options->hostbased_authentication = -1;
436+ options->use_blacklisted_keys = -1;
437 options->batch_mode = -1;
438 options->check_host_ip = -1;
439 options->strict_host_key_checking = -1;
440@@ -1290,6 +1297,8 @@
441 options->rhosts_rsa_authentication = 0;
442 if (options->hostbased_authentication == -1)
443 options->hostbased_authentication = 0;
444+ if (options->use_blacklisted_keys == -1)
445+ options->use_blacklisted_keys = 0;
446 if (options->batch_mode == -1)
447 options->batch_mode = 0;
448 if (options->check_host_ip == -1)
449Index: b/readconf.h
450===================================================================
451--- a/readconf.h
452+++ b/readconf.h
453@@ -58,6 +58,7 @@
454 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
455 char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */
456 int zero_knowledge_password_authentication; /* Try jpake */
457+ int use_blacklisted_keys; /* If true, send */
458 int batch_mode; /* Batch mode: do not ask for passwords. */
459 int check_host_ip; /* Also keep track of keys for IP address */
460 int strict_host_key_checking; /* Strict host key checking. */
461Index: b/servconf.c
462===================================================================
463--- a/servconf.c
464+++ b/servconf.c
465@@ -104,6 +104,7 @@
466 options->password_authentication = -1;
467 options->kbd_interactive_authentication = -1;
468 options->challenge_response_authentication = -1;
469+ options->permit_blacklisted_keys = -1;
470 options->permit_empty_passwd = -1;
471 options->permit_user_env = -1;
472 options->use_login = -1;
473@@ -242,6 +243,8 @@
474 options->kbd_interactive_authentication = 0;
475 if (options->challenge_response_authentication == -1)
476 options->challenge_response_authentication = 1;
477+ if (options->permit_blacklisted_keys == -1)
478+ options->permit_blacklisted_keys = 0;
479 if (options->permit_empty_passwd == -1)
480 options->permit_empty_passwd = 0;
481 if (options->permit_user_env == -1)
482@@ -318,7 +321,7 @@
483 sListenAddress, sAddressFamily,
484 sPrintMotd, sPrintLastLog, sIgnoreRhosts,
485 sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
486- sStrictModes, sEmptyPasswd, sTCPKeepAlive,
487+ sStrictModes, sPermitBlacklistedKeys, sEmptyPasswd, sTCPKeepAlive,
488 sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression,
489 sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
490 sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
491@@ -428,6 +431,7 @@
492 { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
493 { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL },
494 { "strictmodes", sStrictModes, SSHCFG_GLOBAL },
495+ { "permitblacklistedkeys", sPermitBlacklistedKeys, SSHCFG_GLOBAL },
496 { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL },
497 { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL },
498 { "uselogin", sUseLogin, SSHCFG_GLOBAL },
499@@ -1047,6 +1051,10 @@
500 intptr = &options->tcp_keep_alive;
501 goto parse_flag;
502
503+ case sPermitBlacklistedKeys:
504+ intptr = &options->permit_blacklisted_keys;
505+ goto parse_flag;
506+
507 case sEmptyPasswd:
508 intptr = &options->permit_empty_passwd;
509 goto parse_flag;
510@@ -1773,6 +1781,7 @@
511 dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost);
512 dump_cfg_fmtint(sStrictModes, o->strict_modes);
513 dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive);
514+ dump_cfg_fmtint(sPermitBlacklistedKeys, o->permit_blacklisted_keys);
515 dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd);
516 dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env);
517 dump_cfg_fmtint(sUseLogin, o->use_login);
518Index: b/servconf.h
519===================================================================
520--- a/servconf.h
521+++ b/servconf.h
522@@ -113,6 +113,7 @@
523 int challenge_response_authentication;
524 int zero_knowledge_password_authentication;
525 /* If true, permit jpake auth */
526+ int permit_blacklisted_keys; /* If true, permit */
527 int permit_empty_passwd; /* If false, do not permit empty
528 * passwords. */
529 int permit_user_env; /* If true, read ~/.ssh/environment */
530Index: b/ssh-add.1
531===================================================================
532--- a/ssh-add.1
533+++ b/ssh-add.1
534@@ -81,6 +81,10 @@
535 .Nm
536 to work.
537 .Pp
538+Any keys recorded in the blacklist of known-compromised keys (see
539+.Xr ssh-vulnkey 1 )
540+will be refused.
541+.Pp
542 The options are as follows:
543 .Bl -tag -width Ds
544 .It Fl c
545@@ -183,6 +187,7 @@
546 .Xr ssh 1 ,
547 .Xr ssh-agent 1 ,
548 .Xr ssh-keygen 1 ,
549+.Xr ssh-vulnkey 1 ,
550 .Xr sshd 8
551 .Sh AUTHORS
552 OpenSSH is a derivative of the original and free
553Index: b/ssh-add.c
554===================================================================
555--- a/ssh-add.c
556+++ b/ssh-add.c
557@@ -142,7 +142,7 @@
558 add_file(AuthenticationConnection *ac, const char *filename)
559 {
560 Key *private, *cert;
561- char *comment = NULL;
562+ char *comment = NULL, *fp;
563 char msg[1024], *certpath;
564 int fd, perms_ok, ret = -1;
565 Buffer keyblob;
566@@ -218,6 +218,14 @@
567 } else {
568 fprintf(stderr, "Could not add identity: %s\n", filename);
569 }
570+ if (blacklisted_key(private, &fp) == 1) {
571+ fprintf(stderr, "Public key %s blacklisted (see "
572+ "ssh-vulnkey(1)); refusing to add it\n", fp);
573+ xfree(fp);
574+ key_free(private);
575+ xfree(comment);
576+ return -1;
577+ }
578
579
580 /* Now try to add the certificate flavour too */
581Index: b/ssh-keygen.1
582===================================================================
583--- a/ssh-keygen.1
584+++ b/ssh-keygen.1
585@@ -670,6 +670,7 @@
586 .Xr ssh 1 ,
587 .Xr ssh-add 1 ,
588 .Xr ssh-agent 1 ,
589+.Xr ssh-vulnkey 1 ,
590 .Xr moduli 5 ,
591 .Xr sshd 8
592 .Rs
593Index: b/ssh-vulnkey.1
594===================================================================
595--- /dev/null
596+++ b/ssh-vulnkey.1
597@@ -0,0 +1,242 @@
598+.\" Copyright (c) 2008 Canonical Ltd. All rights reserved.
599+.\"
600+.\" Redistribution and use in source and binary forms, with or without
601+.\" modification, are permitted provided that the following conditions
602+.\" are met:
603+.\" 1. Redistributions of source code must retain the above copyright
604+.\" notice, this list of conditions and the following disclaimer.
605+.\" 2. Redistributions in binary form must reproduce the above copyright
606+.\" notice, this list of conditions and the following disclaimer in the
607+.\" documentation and/or other materials provided with the distribution.
608+.\"
609+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
610+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
611+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
612+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
613+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
614+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
615+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
616+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
617+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
618+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
619+.\"
620+.Dd $Mdocdate: May 12 2008 $
621+.Dt SSH-VULNKEY 1
622+.Os
623+.Sh NAME
624+.Nm ssh-vulnkey
625+.Nd check blacklist of compromised keys
626+.Sh SYNOPSIS
627+.Nm
628+.Op Fl q | Fl v
629+.Ar file ...
630+.Nm
631+.Fl a
632+.Sh DESCRIPTION
633+.Nm
634+checks a key against a blacklist of compromised keys.
635+.Pp
636+A substantial number of keys are known to have been generated using a broken
637+version of OpenSSL distributed by Debian which failed to seed its random
638+number generator correctly.
639+Keys generated using these OpenSSL versions should be assumed to be
640+compromised.
641+This tool may be useful in checking for such keys.
642+.Pp
643+Keys that are compromised cannot be repaired; replacements must be generated
644+using
645+.Xr ssh-keygen 1 .
646+Make sure to update
647+.Pa authorized_keys
648+files on all systems where compromised keys were permitted to authenticate.
649+.Pp
650+The argument list will be interpreted as a list of paths to public key files
651+or
652+.Pa authorized_keys
653+files.
654+If no suitable file is found at a given path,
655+.Nm
656+will append
657+.Pa .pub
658+and retry, in case it was given a private key file.
659+If no files are given as arguments,
660+.Nm
661+will check
662+.Pa ~/.ssh/id_rsa ,
663+.Pa ~/.ssh/id_dsa ,
664+.Pa ~/.ssh/identity ,
665+.Pa ~/.ssh/authorized_keys
666+and
667+.Pa ~/.ssh/authorized_keys2 ,
668+as well as the system's host keys if readable.
669+.Pp
670+If
671+.Dq -
672+is given as an argument,
673+.Nm
674+will read from standard input.
675+This can be used to process output from
676+.Xr ssh-keyscan 1 ,
677+for example:
678+.Pp
679+.Dl $ ssh-keyscan -t rsa remote.example.org | ssh-vulnkey -
680+.Pp
681+Unless the
682+.Cm PermitBlacklistedKeys
683+option is used,
684+.Xr sshd 8
685+will reject attempts to authenticate with keys in the compromised list.
686+.Pp
687+The output from
688+.Nm
689+looks like this:
690+.Pp
691+.Bd -literal -offset indent
692+/etc/ssh/ssh_host_key:1: COMPROMISED: RSA1 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx root@host
693+/home/user/.ssh/id_dsa:1: Not blacklisted: DSA 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /home/user/.ssh/id_dsa.pub
694+/home/user/.ssh/authorized_keys:3: Unknown (blacklist file not installed): RSA 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx user@host
695+.Ed
696+.Pp
697+Each line is of the following format (any lines beginning with
698+.Dq #
699+should be ignored by scripts):
700+.Pp
701+.Dl Ar filename : Ns Ar line : Ar status : Ar type Ar size Ar fingerprint Ar comment
702+.Pp
703+It is important to distinguish between the possible values of
704+.Ar status :
705+.Pp
706+.Bl -tag -width Ds
707+.It COMPROMISED
708+These keys are listed in a blacklist file, normally because their
709+corresponding private keys are well-known.
710+Replacements must be generated using
711+.Xr ssh-keygen 1 .
712+.It Not blacklisted
713+A blacklist file exists for this key type and size, but this key is not
714+listed in it.
715+Unless there is some particular reason to believe otherwise, this key
716+may be used safely.
717+(Note that DSA keys used with the broken version of OpenSSL distributed
718+by Debian may be compromised in the event that anyone captured a network
719+trace, even if they were generated with a secure version of OpenSSL.)
720+.It Unknown (blacklist file not installed)
721+No blacklist file exists for this key type and size.
722+You should find a suitable published blacklist and install it before
723+deciding whether this key is safe to use.
724+.El
725+.Pp
726+The options are as follows:
727+.Bl -tag -width Ds
728+.It Fl a
729+Check keys of all users on the system.
730+You will typically need to run
731+.Nm
732+as root to use this option.
733+For each user,
734+.Nm
735+will check
736+.Pa ~/.ssh/id_rsa ,
737+.Pa ~/.ssh/id_dsa ,
738+.Pa ~/.ssh/identity ,
739+.Pa ~/.ssh/authorized_keys
740+and
741+.Pa ~/.ssh/authorized_keys2 .
742+It will also check the system's host keys.
743+.It Fl q
744+Quiet mode.
745+Normally,
746+.Nm
747+outputs the fingerprint of each key scanned, with a description of its
748+status.
749+This option suppresses that output.
750+.It Fl v
751+Verbose mode.
752+Normally,
753+.Nm
754+does not output anything for keys that are not listed in their corresponding
755+blacklist file (although it still produces output for keys for which there
756+is no blacklist file, since their status is unknown).
757+This option causes
758+.Nm
759+to produce output for all keys.
760+.El
761+.Sh EXIT STATUS
762+.Nm
763+will exit zero if any of the given keys were in the compromised list,
764+otherwise non-zero.
765+.Sh BLACKLIST FILE FORMAT
766+The blacklist file may start with comments, on lines starting with
767+.Dq # .
768+After these initial comments, it must follow a strict format:
769+.Pp
770+.Bl -bullet -offset indent -compact
771+.It
772+All the lines must be exactly the same length (20 characters followed by a
773+newline) and must be in sorted order.
774+.It
775+Each line must consist of the lower-case hexadecimal MD5 key fingerprint,
776+without colons, and with the first 12 characters removed (that is, the least
777+significant 80 bits of the fingerprint).
778+.El
779+.Pp
780+The key fingerprint may be generated using
781+.Xr ssh-keygen 1 :
782+.Pp
783+.Dl $ ssh-keygen -l -f /path/to/key
784+.Pp
785+This strict format is necessary to allow the blacklist file to be checked
786+quickly, using a binary-search algorithm.
787+.Sh FILES
788+.Bl -tag -width Ds
789+.It Pa ~/.ssh/id_rsa
790+If present, contains the protocol version 2 RSA authentication identity of
791+the user.
792+.It Pa ~/.ssh/id_dsa
793+If present, contains the protocol version 2 DSA authentication identity of
794+the user.
795+.It Pa ~/.ssh/identity
796+If present, contains the protocol version 1 RSA authentication identity of
797+the user.
798+.It Pa ~/.ssh/authorized_keys
799+If present, lists the public keys (RSA/DSA) that can be used for logging in
800+as this user.
801+.It Pa ~/.ssh/authorized_keys2
802+Obsolete name for
803+.Pa ~/.ssh/authorized_keys .
804+This file may still be present on some old systems, but should not be
805+created if it is missing.
806+.It Pa /etc/ssh/ssh_host_rsa_key
807+If present, contains the protocol version 2 RSA identity of the system.
808+.It Pa /etc/ssh/ssh_host_dsa_key
809+If present, contains the protocol version 2 DSA identity of the system.
810+.It Pa /etc/ssh/ssh_host_key
811+If present, contains the protocol version 1 RSA identity of the system.
812+.It Pa /usr/share/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH
813+If present, lists the blacklisted keys of type
814+.Ar TYPE
815+.Pf ( Dq RSA
816+or
817+.Dq DSA )
818+and bit length
819+.Ar LENGTH .
820+The format of this file is described above.
821+RSA1 keys are converted to RSA before being checked in the blacklist.
822+Note that the fingerprints of RSA1 keys are computed differently, so you
823+will not be able to find them in the blacklist by hand.
824+.It Pa /etc/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH
825+Same as
826+.Pa /usr/share/ssh/blacklist. Ns Ar TYPE Ns Pa - Ns Ar LENGTH ,
827+but may be edited by the system administrator to add new blacklist entries.
828+.El
829+.Sh SEE ALSO
830+.Xr ssh-keygen 1 ,
831+.Xr sshd 8
832+.Sh AUTHORS
833+.An -nosplit
834+.An Colin Watson Aq cjwatson@ubuntu.com
835+.Pp
836+Florian Weimer suggested the option to check keys of all users, and the idea
837+of processing
838+.Xr ssh-keyscan 1
839+output.
840Index: b/ssh-vulnkey.c
841===================================================================
842--- /dev/null
843+++ b/ssh-vulnkey.c
844@@ -0,0 +1,387 @@
845+/*
846+ * Copyright (c) 2008 Canonical Ltd. All rights reserved.
847+ *
848+ * Redistribution and use in source and binary forms, with or without
849+ * modification, are permitted provided that the following conditions
850+ * are met:
851+ * 1. Redistributions of source code must retain the above copyright
852+ * notice, this list of conditions and the following disclaimer.
853+ * 2. Redistributions in binary form must reproduce the above copyright
854+ * notice, this list of conditions and the following disclaimer in the
855+ * documentation and/or other materials provided with the distribution.
856+ *
857+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
858+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
859+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
860+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
861+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
862+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
863+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
864+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
865+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
866+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
867+ */
868+
869+#include "includes.h"
870+
871+#include <sys/types.h>
872+#include <sys/stat.h>
873+
874+#include <errno.h>
875+#include <string.h>
876+#include <stdio.h>
877+#include <fcntl.h>
878+#include <unistd.h>
879+
880+#include <openssl/evp.h>
881+
882+#include "xmalloc.h"
883+#include "ssh.h"
884+#include "log.h"
885+#include "key.h"
886+#include "authfile.h"
887+#include "pathnames.h"
888+#include "uidswap.h"
889+#include "misc.h"
890+
891+extern char *__progname;
892+
893+/* Default files to check */
894+static char *default_host_files[] = {
895+ _PATH_HOST_RSA_KEY_FILE,
896+ _PATH_HOST_DSA_KEY_FILE,
897+ _PATH_HOST_KEY_FILE,
898+ NULL
899+};
900+static char *default_files[] = {
901+ _PATH_SSH_CLIENT_ID_RSA,
902+ _PATH_SSH_CLIENT_ID_DSA,
903+ _PATH_SSH_CLIENT_IDENTITY,
904+ _PATH_SSH_USER_PERMITTED_KEYS,
905+ _PATH_SSH_USER_PERMITTED_KEYS2,
906+ NULL
907+};
908+
909+static int verbosity = 0;
910+
911+static int some_keys = 0;
912+static int some_unknown = 0;
913+static int some_compromised = 0;
914+
915+static void
916+usage(void)
917+{
918+ fprintf(stderr, "usage: %s [-aqv] [file ...]\n", __progname);
919+ fprintf(stderr, "Options:\n");
920+ fprintf(stderr, " -a Check keys of all users.\n");
921+ fprintf(stderr, " -q Quiet mode.\n");
922+ fprintf(stderr, " -v Verbose mode.\n");
923+ exit(1);
924+}
925+
926+static void
927+describe_key(const char *filename, u_long linenum, const char *msg,
928+ Key *key, const char *comment, int min_verbosity)
929+{
930+ char *fp;
931+
932+ fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
933+ if (verbosity >= min_verbosity) {
934+ if (strchr(filename, ':'))
935+ printf("\"%s\"", filename);
936+ else
937+ printf("%s", filename);
938+ printf(":%lu: %s: %s %u %s %s\n", linenum, msg,
939+ key_type(key), key_size(key), fp, comment);
940+ }
941+ xfree(fp);
942+}
943+
944+static int
945+do_key(const char *filename, u_long linenum,
946+ Key *key, const char *comment)
947+{
948+ Key *public;
949+ int blacklist_status;
950+ int ret = 1;
951+
952+ some_keys = 1;
953+
954+ public = key_demote(key);
955+ if (public->type == KEY_RSA1)
956+ public->type = KEY_RSA;
957+
958+ blacklist_status = blacklisted_key(public, NULL);
959+ if (blacklist_status == -1) {
960+ describe_key(filename, linenum,
961+ "Unknown (blacklist file not installed)", key, comment, 0);
962+ some_unknown = 1;
963+ } else if (blacklist_status == 1) {
964+ describe_key(filename, linenum,
965+ "COMPROMISED", key, comment, 0);
966+ some_compromised = 1;
967+ ret = 0;
968+ } else
969+ describe_key(filename, linenum,
970+ "Not blacklisted", key, comment, 1);
971+
972+ key_free(public);
973+
974+ return ret;
975+}
976+
977+static int
978+do_filename(const char *filename, int quiet_open)
979+{
980+ FILE *f;
981+ char line[SSH_MAX_PUBKEY_BYTES];
982+ char *cp;
983+ u_long linenum = 0;
984+ Key *key;
985+ char *comment = NULL;
986+ int found = 0, ret = 1;
987+
988+ /* Copy much of key_load_public's logic here so that we can read
989+ * several keys from a single file (e.g. authorized_keys).
990+ */
991+
992+ if (strcmp(filename, "-") != 0) {
993+ int save_errno;
994+ f = fopen(filename, "r");
995+ save_errno = errno;
996+ if (!f) {
997+ char pubfile[MAXPATHLEN];
998+ if (strlcpy(pubfile, filename, sizeof pubfile) <
999+ sizeof(pubfile) &&
1000+ strlcat(pubfile, ".pub", sizeof pubfile) <
1001+ sizeof(pubfile))
1002+ f = fopen(pubfile, "r");
1003+ }
1004+ errno = save_errno; /* earlier errno is more useful */
1005+ if (!f) {
1006+ if (!quiet_open)
1007+ perror(filename);
1008+ return -1;
1009+ }
1010+ if (verbosity > 0)
1011+ printf("# %s\n", filename);
1012+ } else
1013+ f = stdin;
1014+ while (read_keyfile_line(f, filename, line, sizeof(line),
1015+ &linenum) != -1) {
1016+ int i;
1017+ char *space;
1018+ int type;
1019+ char *end;
1020+
1021+ /* Chop trailing newline. */
1022+ i = strlen(line) - 1;
1023+ if (line[i] == '\n')
1024+ line[i] = '\0';
1025+
1026+ /* Skip leading whitespace, empty and comment lines. */
1027+ for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
1028+ ;
1029+ if (!*cp || *cp == '\n' || *cp == '#')
1030+ continue;
1031+
1032+ /* Cope with ssh-keyscan output and options in
1033+ * authorized_keys files.
1034+ */
1035+ space = strchr(cp, ' ');
1036+ if (!space)
1037+ continue;
1038+ *space = '\0';
1039+ type = key_type_from_name(cp);
1040+ *space = ' ';
1041+ /* Leading number (RSA1) or valid type (RSA/DSA) indicates
1042+ * that we have no host name or options to skip.
1043+ */
1044+ if ((strtol(cp, &end, 10) == 0 || *end != ' ') &&
1045+ type == KEY_UNSPEC) {
1046+ int quoted = 0;
1047+
1048+ for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
1049+ if (*cp == '\\' && cp[1] == '"')
1050+ cp++; /* Skip both */
1051+ else if (*cp == '"')
1052+ quoted = !quoted;
1053+ }
1054+ /* Skip remaining whitespace. */
1055+ for (; *cp == ' ' || *cp == '\t'; cp++)
1056+ ;
1057+ if (!*cp)
1058+ continue;
1059+ }
1060+
1061+ /* Read and process the key itself. */
1062+ key = key_new(KEY_RSA1);
1063+ if (key_read(key, &cp) == 1) {
1064+ while (*cp == ' ' || *cp == '\t')
1065+ cp++;
1066+ if (!do_key(filename, linenum,
1067+ key, *cp ? cp : filename))
1068+ ret = 0;
1069+ found = 1;
1070+ } else {
1071+ key_free(key);
1072+ key = key_new(KEY_UNSPEC);
1073+ if (key_read(key, &cp) == 1) {
1074+ while (*cp == ' ' || *cp == '\t')
1075+ cp++;
1076+ if (!do_key(filename, linenum,
1077+ key, *cp ? cp : filename))
1078+ ret = 0;
1079+ found = 1;
1080+ }
1081+ }
1082+ key_free(key);
1083+ }
1084+ if (f != stdin)
1085+ fclose(f);
1086+
1087+ if (!found && filename) {
1088+ key = key_load_public(filename, &comment);
1089+ if (key) {
1090+ if (!do_key(filename, 1, key, comment))
1091+ ret = 0;
1092+ found = 1;
1093+ }
1094+ if (comment)
1095+ xfree(comment);
1096+ }
1097+
1098+ return ret;
1099+}
1100+
1101+static int
1102+do_host(int quiet_open)
1103+{
1104+ int i;
1105+ struct stat st;
1106+ int ret = 1;
1107+
1108+ for (i = 0; default_host_files[i]; i++) {
1109+ if (stat(default_host_files[i], &st) < 0 && errno == ENOENT)
1110+ continue;
1111+ if (!do_filename(default_host_files[i], quiet_open))
1112+ ret = 0;
1113+ }
1114+
1115+ return ret;
1116+}
1117+
1118+static int
1119+do_user(const char *dir)
1120+{
1121+ int i;
1122+ char *file;
1123+ struct stat st;
1124+ int ret = 1;
1125+
1126+ for (i = 0; default_files[i]; i++) {
1127+ xasprintf(&file, "%s/%s", dir, default_files[i]);
1128+ if (stat(file, &st) < 0 && errno == ENOENT) {
1129+ xfree(file);
1130+ continue;
1131+ }
1132+ if (!do_filename(file, 0))
1133+ ret = 0;
1134+ xfree(file);
1135+ }
1136+
1137+ return ret;
1138+}
1139+
1140+int
1141+main(int argc, char **argv)
1142+{
1143+ int opt, all_users = 0;
1144+ int ret = 1;
1145+ extern int optind;
1146+
1147+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1148+ sanitise_stdfd();
1149+
1150+ __progname = ssh_get_progname(argv[0]);
1151+
1152+ SSLeay_add_all_algorithms();
1153+ log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
1154+
1155+ /* We don't need the RNG ourselves, but symbol references here allow
1156+ * ld to link us properly.
1157+ */
1158+ seed_rng();
1159+
1160+ while ((opt = getopt(argc, argv, "ahqv")) != -1) {
1161+ switch (opt) {
1162+ case 'a':
1163+ all_users = 1;
1164+ break;
1165+ case 'q':
1166+ verbosity--;
1167+ break;
1168+ case 'v':
1169+ verbosity++;
1170+ break;
1171+ case 'h':
1172+ default:
1173+ usage();
1174+ }
1175+ }
1176+
1177+ if (all_users) {
1178+ struct passwd *pw;
1179+
1180+ if (!do_host(0))
1181+ ret = 0;
1182+
1183+ while ((pw = getpwent()) != NULL) {
1184+ if (pw->pw_dir) {
1185+ temporarily_use_uid(pw);
1186+ if (!do_user(pw->pw_dir))
1187+ ret = 0;
1188+ restore_uid();
1189+ }
1190+ }
1191+ } else if (optind == argc) {
1192+ struct passwd *pw;
1193+
1194+ if (!do_host(1))
1195+ ret = 0;
1196+
1197+ if ((pw = getpwuid(geteuid())) == NULL)
1198+ fprintf(stderr, "No user found with uid %u\n",
1199+ (u_int)geteuid());
1200+ else {
1201+ if (!do_user(pw->pw_dir))
1202+ ret = 0;
1203+ }
1204+ } else {
1205+ while (optind < argc)
1206+ if (!do_filename(argv[optind++], 0))
1207+ ret = 0;
1208+ }
1209+
1210+ if (verbosity >= 0) {
1211+ if (some_unknown) {
1212+ printf("#\n");
1213+ printf("# The status of some keys on your system is unknown.\n");
1214+ printf("# You may need to install additional blacklist files.\n");
1215+ }
1216+ if (some_compromised) {
1217+ printf("#\n");
1218+ printf("# Some keys on your system have been compromised!\n");
1219+ printf("# You must replace them using ssh-keygen(1).\n");
1220+ }
1221+ if (some_unknown || some_compromised) {
1222+ printf("#\n");
1223+ printf("# See the ssh-vulnkey(1) manual page for further advice.\n");
1224+ } else if (some_keys && verbosity > 0) {
1225+ printf("#\n");
1226+ printf("# No blacklisted keys!\n");
1227+ }
1228+ }
1229+
1230+ return ret;
1231+}
1232Index: b/ssh.1
1233===================================================================
1234--- a/ssh.1
1235+++ b/ssh.1
1236@@ -1407,6 +1407,7 @@
1237 .Xr ssh-agent 1 ,
1238 .Xr ssh-keygen 1 ,
1239 .Xr ssh-keyscan 1 ,
1240+.Xr ssh-vulnkey 1 ,
1241 .Xr tun 4 ,
1242 .Xr hosts.equiv 5 ,
1243 .Xr ssh_config 5 ,
1244Index: b/ssh.c
1245===================================================================
1246--- a/ssh.c
1247+++ b/ssh.c
1248@@ -1476,7 +1476,7 @@
1249 static void
1250 load_public_identity_files(void)
1251 {
1252- char *filename, *cp, thishost[NI_MAXHOST];
1253+ char *filename, *cp, thishost[NI_MAXHOST], *fp;
1254 char *pwdir = NULL, *pwname = NULL;
1255 int i = 0;
1256 Key *public;
1257@@ -1533,6 +1533,22 @@
1258 public = key_load_public(filename, NULL);
1259 debug("identity file %s type %d", filename,
1260 public ? public->type : -1);
1261+ if (public && blacklisted_key(public, &fp) == 1) {
1262+ if (options.use_blacklisted_keys)
1263+ logit("Public key %s blacklisted (see "
1264+ "ssh-vulnkey(1)); continuing anyway", fp);
1265+ else
1266+ logit("Public key %s blacklisted (see "
1267+ "ssh-vulnkey(1)); refusing to send it",
1268+ fp);
1269+ xfree(fp);
1270+ if (!options.use_blacklisted_keys) {
1271+ key_free(public);
1272+ xfree(filename);
1273+ filename = NULL;
1274+ public = NULL;
1275+ }
1276+ }
1277 xfree(options.identity_files[i]);
1278 identity_files[n_ids] = filename;
1279 identity_keys[n_ids] = public;
1280Index: b/ssh_config.5
1281===================================================================
1282--- a/ssh_config.5
1283+++ b/ssh_config.5
1284@@ -1188,6 +1188,23 @@
1285 .Dq any .
1286 The default is
1287 .Dq any:any .
1288+.It Cm UseBlacklistedKeys
1289+Specifies whether
1290+.Xr ssh 1
1291+should use keys recorded in its blacklist of known-compromised keys (see
1292+.Xr ssh-vulnkey 1 )
1293+for authentication.
1294+If
1295+.Dq yes ,
1296+then attempts to use compromised keys for authentication will be logged but
1297+accepted.
1298+It is strongly recommended that this be used only to install new authorized
1299+keys on the remote system, and even then only with the utmost care.
1300+If
1301+.Dq no ,
1302+then attempts to use compromised keys for authentication will be prevented.
1303+The default is
1304+.Dq no .
1305 .It Cm UsePrivilegedPort
1306 Specifies whether to use a privileged port for outgoing connections.
1307 The argument must be
1308Index: b/sshconnect2.c
1309===================================================================
1310--- a/sshconnect2.c
1311+++ b/sshconnect2.c
1312@@ -1489,6 +1489,8 @@
1313
1314 /* list of keys stored in the filesystem */
1315 for (i = 0; i < options.num_identity_files; i++) {
1316+ if (options.identity_files[i] == NULL)
1317+ continue;
1318 key = options.identity_keys[i];
1319 if (key && key->type == KEY_RSA1)
1320 continue;
1321@@ -1582,7 +1584,7 @@
1322 debug("Offering %s public key: %s", key_type(id->key),
1323 id->filename);
1324 sent = send_pubkey_test(authctxt, id);
1325- } else if (id->key == NULL) {
1326+ } else if (id->key == NULL && id->filename) {
1327 debug("Trying private key: %s", id->filename);
1328 id->key = load_identity_file(id->filename);
1329 if (id->key != NULL) {
1330Index: b/sshd.8
1331===================================================================
1332--- a/sshd.8
1333+++ b/sshd.8
1334@@ -948,6 +948,7 @@
1335 .Xr ssh-agent 1 ,
1336 .Xr ssh-keygen 1 ,
1337 .Xr ssh-keyscan 1 ,
1338+.Xr ssh-vulnkey 1 ,
1339 .Xr chroot 2 ,
1340 .Xr hosts_access 5 ,
1341 .Xr login.conf 5 ,
1342Index: b/sshd.c
1343===================================================================
1344--- a/sshd.c
1345+++ b/sshd.c
1346@@ -1598,6 +1598,11 @@
1347 sensitive_data.host_keys[i] = NULL;
1348 continue;
1349 }
1350+ if (auth_key_is_revoked(key, 1)) {
1351+ key_free(key);
1352+ sensitive_data.host_keys[i] = NULL;
1353+ continue;
1354+ }
1355 switch (key->type) {
1356 case KEY_RSA1:
1357 sensitive_data.ssh1_host_key = key;
1358Index: b/sshd_config.5
1359===================================================================
1360--- a/sshd_config.5
1361+++ b/sshd_config.5
1362@@ -795,6 +795,20 @@
1363 Specifies whether password authentication is allowed.
1364 The default is
1365 .Dq yes .
1366+.It Cm PermitBlacklistedKeys
1367+Specifies whether
1368+.Xr sshd 8
1369+should allow keys recorded in its blacklist of known-compromised keys (see
1370+.Xr ssh-vulnkey 1 ) .
1371+If
1372+.Dq yes ,
1373+then attempts to authenticate with compromised keys will be logged but
1374+accepted.
1375+If
1376+.Dq no ,
1377+then attempts to authenticate with compromised keys will be rejected.
1378+The default is
1379+.Dq no .
1380 .It Cm PermitEmptyPasswords
1381 When password authentication is allowed, it specifies whether the
1382 server allows login to accounts with empty password strings.
diff --git a/debian/patches/ssh1-keepalive.patch b/debian/patches/ssh1-keepalive.patch
new file mode 100644
index 000000000..d5a7fe07a
--- /dev/null
+++ b/debian/patches/ssh1-keepalive.patch
@@ -0,0 +1,65 @@
1Description: Partial server keep-alive implementation for SSH1
2Author: Colin Watson <cjwatson@debian.org>
3Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1712
4Last-Update: 2010-02-27
5
6Index: b/clientloop.c
7===================================================================
8--- a/clientloop.c
9+++ b/clientloop.c
10@@ -545,16 +545,21 @@
11 static void
12 server_alive_check(void)
13 {
14- if (packet_inc_alive_timeouts() > options.server_alive_count_max) {
15- logit("Timeout, server %s not responding.", host);
16- cleanup_exit(255);
17+ if (compat20) {
18+ if (packet_inc_alive_timeouts() > options.server_alive_count_max) {
19+ logit("Timeout, server %s not responding.", host);
20+ cleanup_exit(255);
21+ }
22+ packet_start(SSH2_MSG_GLOBAL_REQUEST);
23+ packet_put_cstring("keepalive@openssh.com");
24+ packet_put_char(1); /* boolean: want reply */
25+ packet_send();
26+ /* Insert an empty placeholder to maintain ordering */
27+ client_register_global_confirm(NULL, NULL);
28+ } else {
29+ packet_send_ignore(0);
30+ packet_send();
31 }
32- packet_start(SSH2_MSG_GLOBAL_REQUEST);
33- packet_put_cstring("keepalive@openssh.com");
34- packet_put_char(1); /* boolean: want reply */
35- packet_send();
36- /* Insert an empty placeholder to maintain ordering */
37- client_register_global_confirm(NULL, NULL);
38 }
39
40 /*
41@@ -614,7 +619,7 @@
42 */
43
44 timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */
45- if (options.server_alive_interval > 0 && compat20)
46+ if (options.server_alive_interval > 0)
47 timeout_secs = options.server_alive_interval;
48 set_control_persist_exit_time();
49 if (control_persist_exit_time > 0) {
50Index: b/ssh_config.5
51===================================================================
52--- a/ssh_config.5
53+++ b/ssh_config.5
54@@ -1089,7 +1089,10 @@
55 .Cm ServerAliveCountMax
56 is left at the default, if the server becomes unresponsive,
57 ssh will disconnect after approximately 45 seconds.
58-This option applies to protocol version 2 only.
59+This option applies to protocol version 2 only; in protocol version
60+1 there is no mechanism to request a response from the server to the
61+server alive messages, so disconnection is the responsibility of the TCP
62+stack.
63 .It Cm ServerAliveInterval
64 Sets a timeout interval in seconds after which if no data has been received
65 from the server,
diff --git a/debian/patches/syslog-level-silent.patch b/debian/patches/syslog-level-silent.patch
new file mode 100644
index 000000000..90ddca4ad
--- /dev/null
+++ b/debian/patches/syslog-level-silent.patch
@@ -0,0 +1,37 @@
1Description: "LogLevel SILENT" compatibility
2 "LogLevel SILENT" (-qq) was introduced in Debian openssh 1:3.0.1p1-1 to
3 match the behaviour of non-free SSH, in which -q does not suppress fatal
4 errors. However, this was unintentionally broken in 1:4.6p1-2 and nobody
5 complained, so we've dropped most of it. The parts that remain are basic
6 configuration file compatibility, and an adjustment to "Pseudo-terminal
7 will not be allocated ..." which should be split out into a separate patch.
8Author: Jonathan David Amery <jdamery@ysolde.ucam.org>
9Author: Matthew Vernon <matthew@debian.org>
10Author: Colin Watson <cjwatson@debian.org>
11Last-Update: 2010-03-31
12
13Index: b/log.c
14===================================================================
15--- a/log.c
16+++ b/log.c
17@@ -92,6 +92,7 @@
18 LogLevel val;
19 } log_levels[] =
20 {
21+ { "SILENT", SYSLOG_LEVEL_QUIET }, /* compatibility */
22 { "QUIET", SYSLOG_LEVEL_QUIET },
23 { "FATAL", SYSLOG_LEVEL_FATAL },
24 { "ERROR", SYSLOG_LEVEL_ERROR },
25Index: b/ssh.c
26===================================================================
27--- a/ssh.c
28+++ b/ssh.c
29@@ -678,7 +678,7 @@
30 /* Do not allocate a tty if stdin is not a tty. */
31 if ((!isatty(fileno(stdin)) || stdin_null_flag) &&
32 options.request_tty != REQUEST_TTY_FORCE) {
33- if (tty_flag)
34+ if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET)
35 logit("Pseudo-terminal will not be allocated because "
36 "stdin is not a terminal.");
37 tty_flag = 0;
diff --git a/debian/patches/user-group-modes.patch b/debian/patches/user-group-modes.patch
new file mode 100644
index 000000000..01ba05526
--- /dev/null
+++ b/debian/patches/user-group-modes.patch
@@ -0,0 +1,202 @@
1Description: Allow harmless group-writability
2 Allow secure files (~/.ssh/config, ~/.ssh/authorized_keys, etc.) to be
3 group-writable, provided that the group in question contains only the
4 file's owner. Rejected upstream for IMO incorrect reasons (e.g. a
5 misunderstanding about the contents of gr->gr_mem). Given that
6 per-user groups and umask 002 are the default setup in Debian (for good
7 reasons - this makes operating in setgid directories with other groups
8 much easier), we need to permit this by default.
9Author: Colin Watson <cjwatson@debian.org>
10Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1060
11Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=314347
12Last-Update: 2010-02-27
13
14Index: b/readconf.c
15===================================================================
16--- a/readconf.c
17+++ b/readconf.c
18@@ -30,6 +30,8 @@
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
22+#include <pwd.h>
23+#include <grp.h>
24
25 #include "xmalloc.h"
26 #include "ssh.h"
27@@ -1131,8 +1133,7 @@
28
29 if (fstat(fileno(f), &sb) == -1)
30 fatal("fstat %s: %s", filename, strerror(errno));
31- if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
32- (sb.st_mode & 022) != 0))
33+ if (!secure_permissions(&sb, getuid()))
34 fatal("Bad owner or permissions on %s", filename);
35 }
36
37Index: b/ssh.1
38===================================================================
39--- a/ssh.1
40+++ b/ssh.1
41@@ -1298,6 +1298,8 @@
42 .Xr ssh_config 5 .
43 Because of the potential for abuse, this file must have strict permissions:
44 read/write for the user, and not accessible by others.
45+It may be group-writable provided that the group in question contains only
46+the user.
47 .Pp
48 .It Pa ~/.ssh/environment
49 Contains additional definitions for environment variables; see
50Index: b/ssh_config.5
51===================================================================
52--- a/ssh_config.5
53+++ b/ssh_config.5
54@@ -1343,6 +1343,8 @@
55 This file is used by the SSH client.
56 Because of the potential for abuse, this file must have strict permissions:
57 read/write for the user, and not accessible by others.
58+It may be group-writable provided that the group in question contains only
59+the user.
60 .It Pa /etc/ssh/ssh_config
61 Systemwide configuration file.
62 This file provides defaults for those
63Index: b/auth.c
64===================================================================
65--- a/auth.c
66+++ b/auth.c
67@@ -380,8 +380,7 @@
68 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
69 if (options.strict_modes &&
70 (stat(user_hostfile, &st) == 0) &&
71- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
72- (st.st_mode & 022) != 0)) {
73+ !secure_permissions(&st, pw->pw_uid)) {
74 logit("Authentication refused for %.100s: "
75 "bad owner or modes for %.200s",
76 pw->pw_name, user_hostfile);
77@@ -442,8 +441,7 @@
78
79 /* check the open file to avoid races */
80 if (fstat(fileno(f), &st) < 0 ||
81- (st.st_uid != 0 && st.st_uid != uid) ||
82- (st.st_mode & 022) != 0) {
83+ !secure_permissions(&st, uid)) {
84 snprintf(err, errlen, "bad ownership or modes for file %s",
85 buf);
86 return -1;
87@@ -458,8 +456,7 @@
88 strlcpy(buf, cp, sizeof(buf));
89
90 if (stat(buf, &st) < 0 ||
91- (st.st_uid != 0 && st.st_uid != uid) ||
92- (st.st_mode & 022) != 0) {
93+ !secure_permissions(&st, uid)) {
94 snprintf(err, errlen,
95 "bad ownership or modes for directory %s", buf);
96 return -1;
97Index: b/misc.c
98===================================================================
99--- a/misc.c
100+++ b/misc.c
101@@ -48,8 +48,9 @@
102 #include <netdb.h>
103 #ifdef HAVE_PATHS_H
104 # include <paths.h>
105-#include <pwd.h>
106 #endif
107+#include <pwd.h>
108+#include <grp.h>
109 #ifdef SSH_TUN_OPENBSD
110 #include <net/if.h>
111 #endif
112@@ -642,6 +643,55 @@
113 }
114
115 int
116+secure_permissions(struct stat *st, uid_t uid)
117+{
118+ if (st->st_uid != 0 && st->st_uid != uid)
119+ return 0;
120+ if ((st->st_mode & 002) != 0)
121+ return 0;
122+ if ((st->st_mode & 020) != 0) {
123+ /* If the file is group-writable, the group in question must
124+ * have exactly one member, namely the file's owner.
125+ * (Zero-member groups are typically used by setgid
126+ * binaries, and are unlikely to be suitable.)
127+ */
128+ struct passwd *pw;
129+ struct group *gr;
130+ int members = 0;
131+
132+ gr = getgrgid(st->st_gid);
133+ if (!gr)
134+ return 0;
135+
136+ /* Check primary group memberships. */
137+ while ((pw = getpwent()) != NULL) {
138+ if (pw->pw_gid == gr->gr_gid) {
139+ ++members;
140+ if (pw->pw_uid != uid)
141+ return 0;
142+ }
143+ }
144+ endpwent();
145+
146+ pw = getpwuid(st->st_uid);
147+ if (!pw)
148+ return 0;
149+
150+ /* Check supplementary group memberships. */
151+ if (gr->gr_mem[0]) {
152+ ++members;
153+ if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
154+ gr->gr_mem[1])
155+ return 0;
156+ }
157+
158+ if (!members)
159+ return 0;
160+ }
161+ return 1;
162+}
163+
164+int
165 tun_open(int tun, int mode)
166 {
167 #if defined(CUSTOM_SYS_TUN_OPEN)
168Index: b/misc.h
169===================================================================
170--- a/misc.h
171+++ b/misc.h
172@@ -103,4 +103,6 @@
173 int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
174 int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
175
176+int secure_permissions(struct stat *st, uid_t uid);
177+
178 #endif /* _MISC_H */
179Index: b/auth-rhosts.c
180===================================================================
181--- a/auth-rhosts.c
182+++ b/auth-rhosts.c
183@@ -256,8 +256,7 @@
184 return 0;
185 }
186 if (options.strict_modes &&
187- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
188- (st.st_mode & 022) != 0)) {
189+ !secure_permissions(&st, pw->pw_uid)) {
190 logit("Rhosts authentication refused for %.100s: "
191 "bad ownership or modes for home directory.", pw->pw_name);
192 auth_debug_add("Rhosts authentication refused for %.100s: "
193@@ -283,8 +282,7 @@
194 * allowing access to their account by anyone.
195 */
196 if (options.strict_modes &&
197- ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
198- (st.st_mode & 022) != 0)) {
199+ !secure_permissions(&st, pw->pw_uid)) {
200 logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
201 pw->pw_name, buf);
202 auth_debug_add("Bad file modes for %.200s", buf);