[gsi-openssh/el6] Based on openssh-5.3p1-94.el6

Mattias Ellert ellert at fedoraproject.org
Tue Nov 26 19:12:08 UTC 2013


commit 336fda7faf3d9e240dfa977531f0072ff0ab6147
Author: Mattias Ellert <mattias.ellert at fysast.uu.se>
Date:   Tue Nov 26 20:02:28 2013 +0100

    Based on openssh-5.3p1-94.el6

 .gitignore                                         |    1 +
 gsi-openssh.spec                                   |   47 +-
 openssh-5.1p1-log-in-chroot.patch                  |   78 +-
 openssh-5.3p1-880575.patch                         |   23 +
 openssh-5.3p1-KexAlgorithms.patch                  |  246 +
 openssh-5.3p1-change-max-startups.patch            |   42 +
 openssh-5.3p1-drop-internal-sftp-connections.patch |   68 +
 openssh-5.3p1-entropy.patch                        |   16 +-
 openssh-5.3p1-fips.patch                           |   52 +-
 openssh-5.3p1-fix-manpage-typos.patch              |   79 +
 openssh-5.3p1-gsissh.patch                         |  322 +-
 openssh-5.3p1-gssapi-with-poly-tmp.patch           |   44 +
 openssh-5.3p1-hmac-sha2.patch                      |  173 +
 openssh-5.3p1-ldap.patch                           |   59 +-
 openssh-5.3p1-pkcs11-support.patch                 | 3536 ++++++++++
 openssh-5.3p1-ssh-agent-fix-race.patch             |   67 +
 openssh-5.3p1-ssh-certificates.patch               | 6741 ++++++++++++++++++++
 openssh-5.3p1-utf8-banner-message.patch            | 1047 +++
 18 files changed, 12418 insertions(+), 223 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index e931757..154e823 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 /*.tar.bz2
+/*.tar.gz
diff --git a/gsi-openssh.spec b/gsi-openssh.spec
index c06f22d..d312333 100644
--- a/gsi-openssh.spec
+++ b/gsi-openssh.spec
@@ -34,10 +34,13 @@
 # Whether or not /sbin/nologin exists.
 %global nologin 1
 
+%global openssh_ver 5.3p1
+%global openssh_rel 10
+
 Summary: An implementation of the SSH protocol with GSI authentication
 Name: gsi-openssh
-Version: 5.3p1
-Release: 9%{?dist}
+Version: %{openssh_ver}
+Release: %{openssh_rel}%{?dist}
 Provides: gsissh = %{version}-%{release}
 Obsoletes: gsissh < 5.3p1-3
 URL: http://www.openssh.com/portable.html
@@ -110,6 +113,29 @@ Patch98: openssh-5.3p1-prevent-post-auth-resource-exhaustion.patch
 Patch99: openssh-5.3p1-v6only.patch
 # Add a 'netcat mode' (ssh -W) (#860809)
 Patch100: openssh-5.3p1-netcat-mode.patch
+# change the bad key permissions error message (#880575)
+Patch101: openssh-5.3p1-880575.patch
+# fix a race condition in ssh-agent (#896561)
+Patch102: openssh-5.3p1-ssh-agent-fix-race.patch
+# backport support for PKCS11 from openssh-5.4p1 (#908038)
+# https://bugzilla.mindrot.org/show_bug.cgi?id=1371
+Patch103: openssh-5.3p1-pkcs11-support.patch
+# add a KexAlgorithms knob to the client and server configuration (#951704)
+Patch104: openssh-5.3p1-KexAlgorithms.patch
+# Add HMAC-SHA2 algorithm support (#969565)
+Patch105: openssh-5.3p1-hmac-sha2.patch
+# Fix man page typos (#896547)
+Patch106: openssh-5.3p1-fix-manpage-typos.patch
+# Add support for certificate key types for users and hosts (#906872)
+Patch107: openssh-5.3p1-ssh-certificates.patch
+# Apply RFC3454 stringprep to banners when possible (#955792)
+Patch108: openssh-5.3p1-utf8-banner-message.patch
+# Abort non-subsystem sessions to forced internal sftp-server (#993509)
+Patch109: openssh-5.3p1-drop-internal-sftp-connections.patch
+# Do ssh_gssapi_krb5_storecreds() twice - before and after pam sesssion (#974096)
+Patch110: openssh-5.3p1-gssapi-with-poly-tmp.patch
+# Change default of MaxStartups to 10:30:100 (#908707)
+Patch111: openssh-5.3p1-change-max-startups.patch
 
 # This is the patch that adds GSI support
 # Based on http://grid.ncsa.illinois.edu/ssh/dl/patch/openssh-5.3p1.patch
@@ -270,6 +296,17 @@ This version of OpenSSH has been modified to support GSI authentication.
 %patch98 -p1 -b .postauth-exhaustion
 %patch99 -p1 -b .v6only
 %patch100 -p1 -b .netcat
+%patch101 -p1 -b .key-perm-message
+%patch102 -p1 -b .fix-race
+%patch103 -p1 -b .pkcs11
+%patch104 -p1 -b .KexAlgorithms
+%patch105 -p1 -b .hmac-sha2
+%patch106 -p1 -b .man
+%patch107 -p1 -b .certificates
+%patch108 -p1 -b .utf8-banner
+%patch109 -p1 -b .drop-internal-sftp
+%patch110 -p1 -b .gssapi-poly-tmp
+%patch111 -p1 -b .max-startups
 
 %patch200 -p1 -b .gsi
 
@@ -383,6 +420,7 @@ rm $RPM_BUILD_ROOT%{_bindir}/ssh-keyscan
 rm $RPM_BUILD_ROOT%{_libexecdir}/gsissh/ssh-keycat
 rm $RPM_BUILD_ROOT%{_libexecdir}/gsissh/ssh-ldap-helper
 rm $RPM_BUILD_ROOT%{_libexecdir}/gsissh/ssh-ldap-wrapper
+rm $RPM_BUILD_ROOT%{_libexecdir}/gsissh/ssh-pkcs11-helper
 rm $RPM_BUILD_ROOT%{_mandir}/man1/ssh-add.1*
 rm $RPM_BUILD_ROOT%{_mandir}/man1/ssh-agent.1*
 rm $RPM_BUILD_ROOT%{_mandir}/man1/ssh-keyscan.1*
@@ -435,7 +473,7 @@ fi
 
 %files
 %defattr(-,root,root)
-%doc CREDITS ChangeLog INSTALL LICENCE LICENSE.globus_usage OVERVIEW PROTOCOL* README* TODO WARNING*
+%doc CREDITS ChangeLog INSTALL LICENCE LICENSE.globus_usage OVERVIEW PROTOCOL PROTOCOL.agent PROTOCOL.certkeys README* TODO WARNING*
 %attr(0755,root,root) %dir %{_sysconfdir}/gsissh
 %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/gsissh/moduli
 %attr(0755,root,root) %{_bindir}/gsissh-keygen
@@ -474,6 +512,9 @@ fi
 %attr(0640,root,root) %config(noreplace) /etc/sysconfig/gsisshd
 
 %changelog
+* Tue Nov 26 2013 Mattias Ellert <mattias.ellert at fysast.uu.se> - 5.3p1-10
+- Based on openssh-5.3p1-94.el6
+
 * Sat Apr 06 2013 Mattias Ellert <mattias.ellert at fysast.uu.se> - 5.3p1-9
 - Security fix for vulnerability
     http://grid.ncsa.illinois.edu/ssh/pamuserchange-2013-01.adv
diff --git a/openssh-5.1p1-log-in-chroot.patch b/openssh-5.1p1-log-in-chroot.patch
index 197fdb9..24449c6 100644
--- a/openssh-5.1p1-log-in-chroot.patch
+++ b/openssh-5.1p1-log-in-chroot.patch
@@ -12,9 +12,9 @@ diff -up openssh-5.1p1/sshd.c.log-chroot openssh-5.1p1/sshd.c
  	/* Change our root directory */
  	if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1)
  		fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR,
-diff -up openssh-5.1p1/log.c.log-chroot openssh-5.1p1/log.c
---- openssh-5.1p1/log.c.log-chroot	2008-06-10 15:01:51.000000000 +0200
-+++ openssh-5.1p1/log.c	2008-07-23 15:18:52.000000000 +0200
+diff -up openssh-5.3p1/log.c.log-chroot openssh-5.3p1/log.c
+--- openssh-5.3p1/log.c.log-chroot	2008-06-10 15:01:51.000000000 +0200
++++ openssh-5.3p1/log.c	2013-04-12 16:46:11.441039273 +0200
 @@ -45,6 +45,7 @@
  #include <syslog.h>
  #include <unistd.h>
@@ -23,7 +23,7 @@ diff -up openssh-5.1p1/log.c.log-chroot openssh-5.1p1/log.c
  #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H)
  # include <vis.h>
  #endif
-@@ -56,6 +57,7 @@
+@@ -56,6 +57,7 @@ static LogLevel log_level = SYSLOG_LEVEL
  static int log_on_stderr = 1;
  static int log_facility = LOG_AUTH;
  static char *argv0;
@@ -31,7 +31,7 @@ diff -up openssh-5.1p1/log.c.log-chroot openssh-5.1p1/log.c
  
  extern char *__progname;
  
-@@ -310,6 +312,8 @@
+@@ -310,6 +312,8 @@ log_init(char *av0, LogLevel level, Sysl
  		exit(1);
  	}
  
@@ -40,7 +40,7 @@ diff -up openssh-5.1p1/log.c.log-chroot openssh-5.1p1/log.c
  	/*
  	 * If an external library (eg libwrap) attempts to use syslog
  	 * immediately after reexec, syslog may be pointing to the wrong
-@@ -392,10 +396,33 @@
+@@ -392,10 +396,34 @@ do_log(LogLevel level, const char *fmt,
  		syslog_r(pri, &sdata, "%.500s", fmtbuf);
  		closelog_r(&sdata);
  #else
@@ -61,23 +61,24 @@ diff -up openssh-5.1p1/log.c.log-chroot openssh-5.1p1/log.c
 +{
 +	int temp1, temp2;
 +
++	closelog();
 +	temp1 = open("/dev/null", O_RDONLY);
 +	openlog(argv0 ? argv0 : __progname, LOG_PID|LOG_NDELAY, log_facility);
 +	temp2 = open("/dev/null", O_RDONLY);
-+	if (temp1 + 2 ==  temp2)
-+		log_fd_keep = temp1 + 1;
++	if (temp1 + 2 <=  temp2)
++		log_fd_keep = temp2 - 1;
 +	else 
-+		log_fd_keep = -1;
++		log_fd_keep = 0;
 +
 +	if (temp1 != -1)
 +		close(temp1);
 +	if (temp2 != -1)
 +		close(temp2);
 +}
-diff -up openssh-5.1p1/log.h.log-chroot openssh-5.1p1/log.h
---- openssh-5.1p1/log.h.log-chroot	2008-06-13 02:22:54.000000000 +0200
-+++ openssh-5.1p1/log.h	2008-07-23 15:20:11.000000000 +0200
-@@ -46,6 +46,9 @@
+diff -up openssh-5.3p1/log.h.log-chroot openssh-5.3p1/log.h
+--- openssh-5.3p1/log.h.log-chroot	2008-06-13 02:22:54.000000000 +0200
++++ openssh-5.3p1/log.h	2013-04-12 16:46:11.441039273 +0200
+@@ -46,6 +46,9 @@ typedef enum {
  	SYSLOG_LEVEL_NOT_SET = -1
  }       LogLevel;
  
@@ -87,24 +88,26 @@ diff -up openssh-5.1p1/log.h.log-chroot openssh-5.1p1/log.h
  void     log_init(char *, LogLevel, SyslogFacility, int);
  
  SyslogFacility	log_facility_number(char *);
-@@ -66,4 +69,6 @@
+@@ -66,4 +69,6 @@ void     debug3(const char *, ...) __att
  
  void	 do_log(LogLevel, const char *, va_list);
  void	 cleanup_exit(int) __attribute__((noreturn));
 +
 +void     open_log(void);
  #endif
---- openssh-5.2p1/session.c.	2009-03-20 18:32:01.004151364 +0100
-+++ openssh-5.2p1/session.c	2009-03-20 19:00:28.328742384 +0100
-@@ -1445,6 +1456,7 @@
+diff -up openssh-5.3p1/session.c.log-chroot openssh-5.3p1/session.c
+--- openssh-5.3p1/session.c.log-chroot	2013-04-16 15:19:36.348871199 +0200
++++ openssh-5.3p1/session.c	2013-04-16 15:19:36.355871167 +0200
+@@ -1440,6 +1440,8 @@ safely_chroot(const char *path, uid_t ui
+ 
+ 	}
+ 
++	open_log ();
++
  	if (chdir(path) == -1)
  		fatal("Unable to chdir to chroot path \"%s\": "
  		    "%s", path, strerror(errno));
-+	open_log ();
- 	if (chroot(path) == -1)
- 		fatal("chroot(\"%s\"): %s", path, strerror(errno));
- 	if (chdir("/") == -1)
-@@ -1632,7 +1644,8 @@
+@@ -1630,7 +1632,8 @@ child_close_fds(void)
  	 * descriptors open.
  	 */
  	for (i = 3; i < 64; i++)
@@ -114,3 +117,34 @@ diff -up openssh-5.1p1/log.h.log-chroot openssh-5.1p1/log.h
  }
  
  /*
+@@ -1720,14 +1723,6 @@ do_child(Session *s, const char *command
+ 	if (options.use_login)
+ 		hostname = get_remote_name_or_ip(utmp_len,
+ 		    options.use_dns);
+-	/*
+-	 * Close the connection descriptors; note that this is the child, and
+-	 * the server will still have the socket open, and it is important
+-	 * that we do not shutdown it.  Note that the descriptors cannot be
+-	 * closed before building the environment, as we call
+-	 * get_remote_ipaddr there.
+-	 */
+-	child_close_fds();
+ 
+ 	/*
+ 	 * Must take new environment into use so that .ssh/rc,
+@@ -1775,7 +1770,14 @@ do_child(Session *s, const char *command
+ 			exit(1);
+ 	}
+ 
+-	closefrom(STDERR_FILENO + 1);
++	/*
++	 * Close the connection descriptors; note that this is the child, and
++	 * the server will still have the socket open, and it is important
++	 * that we do not shutdown it.  Note that the descriptors cannot be
++	 * closed before building the environment, as we call
++	 * get_remote_ipaddr there.
++	 */
++	child_close_fds();
+ 
+ 	if (!options.use_login)
+ 		do_rc_files(s, shell);
diff --git a/openssh-5.3p1-880575.patch b/openssh-5.3p1-880575.patch
new file mode 100644
index 0000000..939eb2b
--- /dev/null
+++ b/openssh-5.3p1-880575.patch
@@ -0,0 +1,23 @@
+commit c87bfe7b479591b14699b53057c7d84f0286411d
+Author: djm <djm>
+Date:   Tue Apr 12 05:39:35 2011 +0000
+
+    s/recommended/required in warning:
+    
+    "It is recommended that your private key files are NOT accessible by others."
+    
+    since there is no way to skip this check; bz#1878
+
+diff --git a/authfile.c b/authfile.c
+index f2aec26..a49850c 100644
+--- a/authfile.c
++++ b/authfile.c
+@@ -606,7 +606,7 @@ key_perm_ok(int fd, const char *filename)
+ 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ 		error("Permissions 0%3.3o for '%s' are too open.",
+ 		    (u_int)st.st_mode & 0777, filename);
+-		error("It is recommended that your private key files are NOT accessible by others.");
++		error("It is required that your private key files are NOT accessible by others.");
+ 		error("This private key will be ignored.");
+ 		return 0;
+ 	}
diff --git a/openssh-5.3p1-KexAlgorithms.patch b/openssh-5.3p1-KexAlgorithms.patch
new file mode 100644
index 0000000..8a5c7b8
--- /dev/null
+++ b/openssh-5.3p1-KexAlgorithms.patch
@@ -0,0 +1,246 @@
+diff -U0 openssh-5.3p1/ChangeLog.KexAlgorithms openssh-5.3p1/ChangeLog
+--- openssh-5.3p1/ChangeLog.KexAlgorithms	2013-07-12 10:05:17.178031967 +0200
++++ openssh-5.3p1/ChangeLog	2013-07-12 10:05:17.209031967 +0200
+@@ -0,0 +1,9 @@
++20100924
++   - djm at cvs.openbsd.org 2010/09/22 05:01:30
++     [kex.c kex.h kexecdh.c kexecdhc.c kexecdhs.c readconf.c readconf.h]
++     [servconf.c servconf.h ssh_config.5 sshconnect2.c sshd.c sshd_config.5]
++     add a KexAlgorithms knob to the client and server configuration to allow
++     selection of which key exchange methods are used by ssh(1) and sshd(8)
++     and their order of preference.
++     ok markus@
++
+diff -up openssh-5.3p1/kex.c.KexAlgorithms openssh-5.3p1/kex.c
+--- openssh-5.3p1/kex.c.KexAlgorithms	2013-07-12 10:05:17.004031967 +0200
++++ openssh-5.3p1/kex.c	2013-07-12 10:07:55.152031912 +0200
+@@ -66,6 +66,31 @@ extern const EVP_MD *evp_ssh_sha256(void
+ static void kex_kexinit_finish(Kex *);
+ static void kex_choose_conf(Kex *);
+ 
++/* Validate KEX method name list */
++int
++kex_names_valid(const char *names)
++{
++	char *s, *cp, *p;
++
++	if (names == NULL || strcmp(names, "") == 0)
++		return 0;
++	s = cp = xstrdup(names);
++	for ((p = strsep(&cp, ",")); p && *p != '\0';
++	    (p = strsep(&cp, ","))) {
++	    	if (strcmp(p, KEX_DHGEX_SHA256) != 0 &&
++		    strcmp(p, KEX_DHGEX_SHA1) != 0 &&
++		    strcmp(p, KEX_DH14) != 0 &&
++		    strcmp(p, KEX_DH1) != 0 ) {
++			error("Unsupported KEX algorithm \"%.100s\"", p);
++			xfree(s);
++			return 0;
++		}
++	}
++	debug3("kex names ok: [%s]", names);
++	xfree(s);
++	return 1;
++}
++
+ /* put algorithm proposal into buffer */
+ static void
+ kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
+diff -up openssh-5.3p1/kex.h.KexAlgorithms openssh-5.3p1/kex.h
+--- openssh-5.3p1/kex.h.KexAlgorithms	2013-07-12 10:05:17.005031967 +0200
++++ openssh-5.3p1/kex.h	2013-07-12 10:05:17.210031967 +0200
+@@ -138,6 +138,8 @@ struct Kex {
+ 	void	(*kex[KEX_MAX])(Kex *);
+ };
+ 
++int	 kex_names_valid(const char *);
++
+ Kex	*kex_setup(char *[PROPOSAL_MAX]);
+ void	 kex_finish(Kex *);
+ 
+diff -up openssh-5.3p1/readconf.c.KexAlgorithms openssh-5.3p1/readconf.c
+--- openssh-5.3p1/readconf.c.KexAlgorithms	2013-07-12 10:05:17.186031967 +0200
++++ openssh-5.3p1/readconf.c	2013-07-12 10:05:17.211031967 +0200
+@@ -133,6 +133,7 @@ typedef enum {
+ 	oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
+ 	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
+ 	oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
++	oKexAlgorithms,
+ 	oDeprecated, oUnsupported
+ } OpCodes;
+ 
+@@ -256,6 +257,7 @@ static struct {
+ #else
+ 	{ "zeroknowledgepasswordauthentication", oUnsupported },
+ #endif
++	{ "kexalgorithms", oKexAlgorithms },
+ 
+ 	{ NULL, oBadOption }
+ };
+@@ -738,6 +740,18 @@ parse_int:
+ 			options->macs = xstrdup(arg);
+ 		break;
+ 
++	case oKexAlgorithms:
++		arg = strdelim(&s);
++		if (!arg || *arg == '\0')
++			fatal("%.200s line %d: Missing argument.",
++			    filename, linenum);
++		if (!kex_names_valid(arg))
++			fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
++			    filename, linenum, arg ? arg : "<NONE>");
++		if (*activep && options->kex_algorithms == NULL)
++			options->kex_algorithms = xstrdup(arg);
++		break;
++
+ 	case oHostKeyAlgorithms:
+ 		arg = strdelim(&s);
+ 		if (!arg || *arg == '\0')
+@@ -1096,6 +1110,7 @@ initialize_options(Options * options)
+ 	options->cipher = -1;
+ 	options->ciphers = NULL;
+ 	options->macs = NULL;
++	options->kex_algorithms = NULL;
+ 	options->hostkeyalgorithms = NULL;
+ 	options->protocol = SSH_PROTO_UNKNOWN;
+ 	options->num_identity_files = 0;
+@@ -1212,6 +1227,7 @@ fill_default_options(Options * options)
+ 		options->cipher = SSH_CIPHER_NOT_SET;
+ 	/* options->ciphers, default set in myproposals.h */
+ 	/* options->macs, default set in myproposals.h */
++	/* options->kex_algorithms, default set in myproposals.h */
+ 	/* options->hostkeyalgorithms, default set in myproposals.h */
+ 	if (options->protocol == SSH_PROTO_UNKNOWN)
+ 		options->protocol = SSH_PROTO_1|SSH_PROTO_2;
+diff -up openssh-5.3p1/readconf.h.KexAlgorithms openssh-5.3p1/readconf.h
+--- openssh-5.3p1/readconf.h.KexAlgorithms	2013-07-12 10:05:17.187031967 +0200
++++ openssh-5.3p1/readconf.h	2013-07-12 10:05:17.212031967 +0200
+@@ -75,6 +75,7 @@ typedef struct {
+ 	char   *ciphers;	/* SSH2 ciphers in order of preference. */
+ 	char   *macs;		/* SSH2 macs in order of preference. */
+ 	char   *hostkeyalgorithms;	/* SSH2 server key types in order of preference. */
++	char   *kex_algorithms;	/* SSH2 kex methods in order of preference. */
+ 	int	protocol;	/* Protocol in order of preference. */
+ 	char   *hostname;	/* Real host to connect. */
+ 	char   *host_key_alias;	/* hostname alias for .ssh/known_hosts */
+diff -up openssh-5.3p1/servconf.c.KexAlgorithms openssh-5.3p1/servconf.c
+--- openssh-5.3p1/servconf.c.KexAlgorithms	2013-07-12 10:05:17.142031967 +0200
++++ openssh-5.3p1/servconf.c	2013-07-12 10:05:17.214031967 +0200
+@@ -113,6 +113,7 @@ initialize_server_options(ServerOptions 
+ 	options->num_deny_groups = 0;
+ 	options->ciphers = NULL;
+ 	options->macs = NULL;
++	options->kex_algorithms = NULL;
+ 	options->protocol = SSH_PROTO_UNKNOWN;
+ 	options->gateway_ports = -1;
+ 	options->num_subsystems = 0;
+@@ -332,6 +333,7 @@ typedef enum {
+ 	sUsePrivilegeSeparation, sAllowAgentForwarding,
+ 	sZeroKnowledgePasswordAuthentication,
+ 	sAuthorizedKeysCommand, sAuthorizedKeysCommandRunAs,
++	sKexAlgorithms,
+ 	sDeprecated, sUnsupported
+ } ServerOpCodes;
+ 
+@@ -468,6 +470,7 @@ static struct {
+ #endif
+ 	{ "requiredauthentications1", sRequiredAuthentications1, SSHCFG_ALL },
+ 	{ "requiredauthentications2", sRequiredAuthentications2, SSHCFG_ALL },
++	{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
+ 	{ NULL, sBadOption, 0 }
+ };
+ 
+@@ -1154,6 +1157,18 @@ process_server_config_line(ServerOptions
+ 			options->macs = xstrdup(arg);
+ 		break;
+ 
++	case sKexAlgorithms:
++		arg = strdelim(&cp);
++		if (!arg || *arg == '\0')
++			fatal("%s line %d: Missing argument.",
++			    filename, linenum);
++		if (!kex_names_valid(arg))
++			fatal("%s line %d: Bad SSH2 KexAlgorithms '%s'.",
++			    filename, linenum, arg ? arg : "<NONE>");
++		if (options->kex_algorithms == NULL)
++			options->kex_algorithms = xstrdup(arg);
++		break;
++
+ 	case sProtocol:
+ 		intptr = &options->protocol;
+ 		arg = strdelim(&cp);
+diff -up openssh-5.3p1/servconf.h.KexAlgorithms openssh-5.3p1/servconf.h
+--- openssh-5.3p1/servconf.h.KexAlgorithms	2013-07-12 10:05:17.143031967 +0200
++++ openssh-5.3p1/servconf.h	2013-07-12 10:05:17.215031967 +0200
+@@ -69,6 +69,7 @@ typedef struct {
+ 	int     tcp_keep_alive;	/* If true, set SO_KEEPALIVE. */
+ 	char   *ciphers;	/* Supported SSH2 ciphers. */
+ 	char   *macs;		/* Supported SSH2 macs. */
++	char   *kex_algorithms;	/* SSH2 kex methods in order of preference. */
+ 	int	protocol;	/* Supported protocol versions. */
+ 	int     gateway_ports;	/* If true, allow remote connects to forwarded ports. */
+ 	SyslogFacility log_facility;	/* Facility for system logging. */
+diff -up openssh-5.3p1/ssh_config.5.KexAlgorithms openssh-5.3p1/ssh_config.5
+--- openssh-5.3p1/ssh_config.5.KexAlgorithms	2013-07-12 10:05:17.195031967 +0200
++++ openssh-5.3p1/ssh_config.5	2013-07-12 10:05:17.216031967 +0200
+@@ -623,6 +623,17 @@ it may be zero or more of:
+ .Dq pam ,
+ and
+ .Dq skey .
++.It Cm KexAlgorithms
++Specifies the available KEX (Key Exchange) algorithms.
++Multiple algorithms must be comma-separated.
++The default is
++.Dq ecdh-sha2-nistp256 ,
++.Dq ecdh-sha2-nistp384 ,
++.Dq ecdh-sha2-nistp521 ,
++.Dq diffie-hellman-group-exchange-sha256 , 
++.Dq diffie-hellman-group-exchange-sha1 ,
++.Dq diffie-hellman-group14-sha1 ,
++.Dq diffie-hellman-group1-sha1 .
+ .It Cm LocalCommand
+ Specifies a command to execute on the local machine after successfully
+ connecting to the server.
+diff -up openssh-5.3p1/sshconnect2.c.KexAlgorithms openssh-5.3p1/sshconnect2.c
+--- openssh-5.3p1/sshconnect2.c.KexAlgorithms	2013-07-12 10:05:17.153031967 +0200
++++ openssh-5.3p1/sshconnect2.c	2013-07-12 10:05:17.218031967 +0200
+@@ -171,6 +171,9 @@ ssh_kex2(char *host, struct sockaddr *ho
+ 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
+ 		    options.hostkeyalgorithms;
+ 
++	if (options.kex_algorithms != NULL)
++		myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
++
+ #ifdef GSSAPI
+ 	/* If we've got GSSAPI algorithms, then we also support the
+ 	 * 'null' hostkey, as a last resort */
+diff -up openssh-5.3p1/sshd.c.KexAlgorithms openssh-5.3p1/sshd.c
+--- openssh-5.3p1/sshd.c.KexAlgorithms	2013-07-12 10:05:17.127031967 +0200
++++ openssh-5.3p1/sshd.c	2013-07-12 10:05:17.220031967 +0200
+@@ -2353,6 +2353,8 @@ do_ssh2_kex(void)
+ 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
+ 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib at openssh.com";
+ 	}
++	if (options.kex_algorithms != NULL)
++		myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
+ 
+ 	myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
+ 
+diff -up openssh-5.3p1/sshd_config.5.KexAlgorithms openssh-5.3p1/sshd_config.5
+--- openssh-5.3p1/sshd_config.5.KexAlgorithms	2013-07-12 10:05:17.144031967 +0200
++++ openssh-5.3p1/sshd_config.5	2013-07-12 10:05:17.221031967 +0200
+@@ -515,6 +515,14 @@ The default is
+ Specifies whether to look at .k5login file for user's aliases.
+ The default is
+ .Dq yes .
++.It Cm KexAlgorithms
++Specifies the available KEX (Key Exchange) algorithms.
++Multiple algorithms must be comma-separated.
++The default is
++.Dq diffie-hellman-group-exchange-sha256 , 
++.Dq diffie-hellman-group-exchange-sha1 ,
++.Dq diffie-hellman-group14-sha1 ,
++.Dq diffie-hellman-group1-sha1 .
+ .It Cm KeyRegenerationInterval
+ In protocol version 1, the ephemeral server key is automatically regenerated
+ after this many seconds (if it has been used).
diff --git a/openssh-5.3p1-change-max-startups.patch b/openssh-5.3p1-change-max-startups.patch
new file mode 100644
index 0000000..3eec59f
--- /dev/null
+++ b/openssh-5.3p1-change-max-startups.patch
@@ -0,0 +1,42 @@
+diff -up openssh-5.3p1/servconf.c.max-startups openssh-5.3p1/servconf.c
+--- openssh-5.3p1/servconf.c.max-startups	2013-08-28 18:38:18.942667218 +0200
++++ openssh-5.3p1/servconf.c	2013-08-28 18:38:18.959667146 +0200
+@@ -256,11 +256,11 @@ fill_default_server_options(ServerOption
+ 	if (options->gateway_ports == -1)
+ 		options->gateway_ports = 0;
+ 	if (options->max_startups == -1)
+-		options->max_startups = 10;
++		options->max_startups = 100;
+ 	if (options->max_startups_rate == -1)
+-		options->max_startups_rate = 100;		/* 100% */
++		options->max_startups_rate = 30;		/* 30% */
+ 	if (options->max_startups_begin == -1)
+-		options->max_startups_begin = options->max_startups;
++		options->max_startups_begin = 10;
+ 	if (options->max_authtries == -1)
+ 		options->max_authtries = DEFAULT_AUTH_FAIL_MAX;
+ 	if (options->max_sessions == -1)
+diff -up openssh-5.3p1/sshd_config.5.max-startups openssh-5.3p1/sshd_config.5
+--- openssh-5.3p1/sshd_config.5.max-startups	2013-08-28 18:38:18.946667201 +0200
++++ openssh-5.3p1/sshd_config.5	2013-08-28 18:38:18.960667142 +0200
+@@ -726,7 +726,7 @@ SSH daemon.
+ Additional connections will be dropped until authentication succeeds or the
+ .Cm LoginGraceTime
+ expires for a connection.
+-The default is 10.
++The default is 10:30:100.
+ .Pp
+ Alternatively, random early drop can be enabled by specifying
+ the three colon separated values
+diff -up openssh-5.3p1/sshd_config.max-startups openssh-5.3p1/sshd_config
+--- openssh-5.3p1/sshd_config.max-startups	2013-08-28 18:38:18.852667599 +0200
++++ openssh-5.3p1/sshd_config	2013-08-28 18:38:18.960667142 +0200
+@@ -121,7 +121,7 @@ X11Forwarding yes
+ #ShowPatchLevel no
+ #UseDNS yes
+ #PidFile /var/run/sshd.pid
+-#MaxStartups 10
++#MaxStartups 10:30:100
+ #PermitTunnel no
+ #ChrootDirectory none
+ 
diff --git a/openssh-5.3p1-drop-internal-sftp-connections.patch b/openssh-5.3p1-drop-internal-sftp-connections.patch
new file mode 100644
index 0000000..dfe7fb3
--- /dev/null
+++ b/openssh-5.3p1-drop-internal-sftp-connections.patch
@@ -0,0 +1,68 @@
+diff -U0 openssh-5.3p1/ChangeLog.drop-internal-sftp openssh-5.3p1/ChangeLog
+--- openssh-5.3p1/ChangeLog.drop-internal-sftp	2013-08-14 08:35:24.000000000 +0200
++++ openssh-5.3p1/ChangeLog	2013-08-14 08:44:53.057370306 +0200
+@@ -234,0 +235,8 @@
++20100108
++   - djm at cvs.openbsd.org 2009/11/19 23:39:50
++     [session.c]
++     bz#1606: error when an attempt is made to connect to a server
++     with ForceCommand=internal-sftp with a shell session (i.e. not a
++     subsystem session). Avoids stuck client when attempting to ssh to such a
++     service. ok dtucker@
++
+diff -up openssh-5.3p1/session.c.drop-internal-sftp openssh-5.3p1/session.c
+--- openssh-5.3p1/session.c.drop-internal-sftp	2013-08-14 08:35:24.827370511 +0200
++++ openssh-5.3p1/session.c	2013-08-14 08:35:24.993370511 +0200
+@@ -143,9 +143,10 @@ static int sessions_first_unused = -1;
+ static int sessions_nalloc = 0;
+ static Session *sessions = NULL;
+ 
+-#define SUBSYSTEM_NONE		0
+-#define SUBSYSTEM_EXT		1
+-#define SUBSYSTEM_INT_SFTP	2
++#define SUBSYSTEM_NONE			0
++#define SUBSYSTEM_EXT			1
++#define SUBSYSTEM_INT_SFTP		2
++#define SUBSYSTEM_INT_SFTP_ERROR	3
+ 
+ #ifdef HAVE_LOGIN_CAP
+ login_cap_t *lc;
+@@ -794,17 +795,19 @@ do_exec(Session *s, const char *command)
+ 	if (options.adm_forced_command) {
+ 		original_command = command;
+ 		command = options.adm_forced_command;
+-		if (IS_INTERNAL_SFTP(command))
+-			s->is_subsystem = SUBSYSTEM_INT_SFTP;
+-		else if (s->is_subsystem)
++		if (IS_INTERNAL_SFTP(command)) {
++			s->is_subsystem = s->is_subsystem ?
++			    SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR;
++		} else if (s->is_subsystem)
+ 			s->is_subsystem = SUBSYSTEM_EXT;
+ 		debug("Forced command (config) '%.900s'", command);
+ 	} else if (forced_command) {
+ 		original_command = command;
+ 		command = forced_command;
+-		if (IS_INTERNAL_SFTP(command))
+-			s->is_subsystem = SUBSYSTEM_INT_SFTP;
+-		else if (s->is_subsystem)
++		if (IS_INTERNAL_SFTP(command)) {
++			s->is_subsystem = s->is_subsystem ?
++			    SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR;
++		} else if (s->is_subsystem)
+ 			s->is_subsystem = SUBSYSTEM_EXT;
+ 		debug("Forced command (key option) '%.900s'", command);
+ 	}
+@@ -1812,7 +1815,11 @@ do_child(Session *s, const char *command
+ 	/* restore SIGPIPE for child */
+ 	signal(SIGPIPE, SIG_DFL);
+ 
+-	if (s->is_subsystem == SUBSYSTEM_INT_SFTP) {
++	if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) {
++		printf("This service allows sftp connections only.\n");
++		fflush(NULL);
++		exit(1);
++	} else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) {
+ 		extern int optind, optreset;
+ 		int i;
+ 		char *p, *args;
diff --git a/openssh-5.3p1-entropy.patch b/openssh-5.3p1-entropy.patch
index 214702f..82308c3 100644
--- a/openssh-5.3p1-entropy.patch
+++ b/openssh-5.3p1-entropy.patch
@@ -71,11 +71,11 @@ diff -up openssh-5.3p1/openbsd-compat/port-linux-prng.c.entropy openssh-5.3p1/op
 +	int len;
 +	char *env = getenv("SSH_USE_STRONG_RNG");
 +	char *random = "/dev/random";
-+	size_t ienv, randlen = 6;
++	size_t ienv, randlen = 14;
 +
 +	if (!env || !strcmp(env, "0"))
 +		random = "/dev/urandom";
-+	else if ((ienv = atoi(env)) > 6)
++	else if ((ienv = atoi(env)) > randlen)
 +		randlen = ienv;
 +
 +	errno = 0;
@@ -105,7 +105,7 @@ diff -up openssh-5.3p1/ssh.1.entropy openssh-5.3p1/ssh.1
 +the OpenSSL random generator is reseeded from
 +.Cm /dev/random .
 +The number of bytes read is defined by the SSH_USE_STRONG_RNG value. 
-+Minimum is 6 bytes.
++Minimum is 14 bytes.
 +This setting is not recommended on the computers without the hardware
 +random generator because insufficient entropy causes the connection to 
 +be blocked until enough entropy is available.
@@ -130,7 +130,7 @@ diff -up openssh-5.3p1/ssh-add.1.entropy openssh-5.3p1/ssh-add.1
 +the OpenSSL random generator is reseeded from
 +.Cm /dev/random .
 +The number of bytes read is defined by the SSH_USE_STRONG_RNG value. 
-+Minimum is 6 bytes.
++Minimum is 14 bytes.
 +This setting is not recommended on the computers without the hardware
 +random generator because insufficient entropy causes the connection to 
 +be blocked until enough entropy is available.
@@ -157,7 +157,7 @@ diff -up openssh-5.3p1/ssh-agent.1.entropy openssh-5.3p1/ssh-agent.1
 +the OpenSSL random generator is reseeded from
 +.Cm /dev/random .
 +The number of bytes read is defined by the SSH_USE_STRONG_RNG value. 
-+Minimum is 6 bytes.
++Minimum is 14 bytes.
 +This setting is not recommended on the computers without the hardware
 +random generator because insufficient entropy causes the connection to 
 +be blocked until enough entropy is available.
@@ -185,7 +185,7 @@ diff -up openssh-5.3p1/sshd.8.entropy openssh-5.3p1/sshd.8
 +the OpenSSL random generator is reseeded from
 +.Cm /dev/random .
 +The number of bytes read is defined by the SSH_USE_STRONG_RNG value. 
-+Minimum is 6 bytes.
++Minimum is 14 bytes.
 +This setting is not recommended on the computers without the hardware
 +random generator because insufficient entropy causes the connection to 
 +be blocked until enough entropy is available.
@@ -213,7 +213,7 @@ diff -up openssh-5.3p1/ssh-keygen.1.entropy openssh-5.3p1/ssh-keygen.1
 +the OpenSSL random generator is reseeded from
 +.Cm /dev/random .
 +The number of bytes read is defined by the SSH_USE_STRONG_RNG value. 
-+Minimum is 6 bytes.
++Minimum is 14 bytes.
 +This setting is not recommended on the computers without the hardware
 +random generator because insufficient entropy causes the connection to 
 +be blocked until enough entropy is available.
@@ -241,7 +241,7 @@ diff -up openssh-5.3p1/ssh-keysign.8.entropy openssh-5.3p1/ssh-keysign.8
 +the OpenSSL random generator is reseeded from
 +.Cm /dev/random .
 +The number of bytes read is defined by the SSH_USE_STRONG_RNG value. 
-+Minimum is 6 bytes.
++Minimum is 14 bytes.
 +This setting is not recommended on the computers without the hardware
 +random generator because insufficient entropy causes the connection to 
 +be blocked until enough entropy is available.
diff --git a/openssh-5.3p1-fips.patch b/openssh-5.3p1-fips.patch
index 01a715c..1cec453 100644
--- a/openssh-5.3p1-fips.patch
+++ b/openssh-5.3p1-fips.patch
@@ -389,18 +389,28 @@ diff -up openssh-5.3p1/ssh.c.fips openssh-5.3p1/ssh.c
  #include "openbsd-compat/openssl-compat.h"
  #include "openbsd-compat/sys-queue.h"
  
-@@ -221,6 +223,10 @@ main(int ac, char **av)
+@@ -216,11 +218,20 @@ main(int ac, char **av)
+ 	extern char *optarg;
+ 	struct servent *sp;
+ 	Forward fwd;
++	int fips;
+ 
+ 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
  	sanitise_stdfd();
  
  	__progname = ssh_get_progname(av[0]);
++
 +        SSLeay_add_all_algorithms();
-+        if (FIPS_mode() && !FIPSCHECK_verify(NULL, NULL)) {
-+                fatal("FIPS integrity verification test failed.");
-+        }
++	fips = 0;
++	if (access("/usr/share/dracut/modules.d/01fips", F_OK) == 0)
++		fips = FIPSCHECK_verify(NULL, NULL);
++	if (FIPS_mode() && !fips)
++		fatal("FIPS integrity verification test failed.");
++
  	init_rng();
  
  	/*
-@@ -281,6 +287,9 @@ main(int ac, char **av)
+@@ -281,6 +292,9 @@ main(int ac, char **av)
  	    "ACD:F:I:KL:MNO:PR:S:TVw:XYy")) != -1) {
  		switch (opt) {
  		case '1':
@@ -410,7 +420,7 @@ diff -up openssh-5.3p1/ssh.c.fips openssh-5.3p1/ssh.c
  			options.protocol = SSH_PROTO_1;
  			break;
  		case '2':
-@@ -552,7 +561,6 @@ main(int ac, char **av)
+@@ -552,7 +566,6 @@ main(int ac, char **av)
  	if (!host)
  		usage();
  
@@ -418,7 +428,7 @@ diff -up openssh-5.3p1/ssh.c.fips openssh-5.3p1/ssh.c
  	ERR_load_crypto_strings();
  
  	/* Initialize the command to execute on remote host. */
-@@ -638,6 +646,10 @@ main(int ac, char **av)
+@@ -638,6 +651,10 @@ main(int ac, char **av)
  
  	seed_rng();
  
@@ -429,7 +439,7 @@ diff -up openssh-5.3p1/ssh.c.fips openssh-5.3p1/ssh.c
  	if (options.user == NULL)
  		options.user = xstrdup(pw->pw_name);
  
-@@ -704,6 +716,12 @@ main(int ac, char **av)
+@@ -704,6 +721,12 @@ main(int ac, char **av)
  
  	timeout_ms = options.connection_timeout * 1000;
  
@@ -589,20 +599,28 @@ diff -up openssh-5.3p1/sshd.c.fips openssh-5.3p1/sshd.c
  #include "openbsd-compat/openssl-compat.h"
  
  #ifdef HAVE_SECUREWARE
-@@ -1261,6 +1263,12 @@ main(int ac, char **av)
+@@ -1256,11 +1258,20 @@ main(int ac, char **av)
+ 	mode_t new_umask;
+ 	Key *key;
+ 	Authctxt *authctxt;
++	int fips;
+ 
+ #ifdef HAVE_SECUREWARE
  	(void)set_auth_parameters(ac, av);
  #endif
  	__progname = ssh_get_progname(av[0]);
 +
 +        SSLeay_add_all_algorithms();
-+        if (FIPS_mode() && !FIPSCHECK_verify(NULL, NULL)) {
-+                fatal("FIPS integrity verification test failed.");
-+        }
++	fips = 0;
++	if (access("/usr/share/dracut/modules.d/01fips", F_OK) == 0)
++		fips = FIPSCHECK_verify(NULL, NULL);
++	if (FIPS_mode() && !fips)
++		fatal("FIPS integrity verification test failed.");
 +
  	init_rng();
  
  	/* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
-@@ -1413,8 +1421,6 @@ main(int ac, char **av)
+@@ -1413,8 +1424,6 @@ main(int ac, char **av)
  	else
  		closefrom(REEXEC_DEVCRYPTO_RESERVED_FD);
  
@@ -611,7 +629,7 @@ diff -up openssh-5.3p1/sshd.c.fips openssh-5.3p1/sshd.c
  	/*
  	 * Force logging to stderr until we have loaded the private host
  	 * key (unless started from inetd)
-@@ -1532,6 +1538,10 @@ main(int ac, char **av)
+@@ -1532,6 +1541,10 @@ main(int ac, char **av)
  		debug("private host key: #%d type %d %s", i, key->type,
  		    key_type(key));
  	}
@@ -622,7 +640,7 @@ diff -up openssh-5.3p1/sshd.c.fips openssh-5.3p1/sshd.c
  	if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) {
  		logit("Disabling protocol version 1. Could not load host key");
  		options.protocol &= ~SSH_PROTO_1;
-@@ -1656,6 +1666,10 @@ main(int ac, char **av)
+@@ -1656,6 +1669,10 @@ main(int ac, char **av)
  	/* Initialize the random number generator. */
  	arc4random_stir();
  
@@ -633,7 +651,7 @@ diff -up openssh-5.3p1/sshd.c.fips openssh-5.3p1/sshd.c
  	/* Chdir to the root directory so that the current disk can be
  	   unmounted if desired. */
  	chdir("/");
-@@ -2183,6 +2197,9 @@ do_ssh2_kex(void)
+@@ -2187,6 +2204,9 @@ do_ssh2_kex(void)
  	if (options.ciphers != NULL) {
  		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
  		myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
@@ -643,7 +661,7 @@ diff -up openssh-5.3p1/sshd.c.fips openssh-5.3p1/sshd.c
  	}
  	myproposal[PROPOSAL_ENC_ALGS_CTOS] =
  	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
-@@ -2192,6 +2209,9 @@ do_ssh2_kex(void)
+@@ -2196,6 +2216,9 @@ do_ssh2_kex(void)
  	if (options.macs != NULL) {
  		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
  		myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
diff --git a/openssh-5.3p1-fix-manpage-typos.patch b/openssh-5.3p1-fix-manpage-typos.patch
new file mode 100644
index 0000000..fc8a9d0
--- /dev/null
+++ b/openssh-5.3p1-fix-manpage-typos.patch
@@ -0,0 +1,79 @@
+diff -up openssh-5.3p1/moduli.5.man openssh-5.3p1/moduli.5
+--- openssh-5.3p1/moduli.5.man	2013-07-18 14:28:56.723256301 +0200
++++ openssh-5.3p1/moduli.5	2013-07-18 14:30:40.266807266 +0200
+@@ -68,7 +68,7 @@ Sophie Germain; (p+1)*2 is also prime.
+ Moduli candidates initially produced by
+ .Xr ssh-keygen 1
+ are Sophie Germain primes (type 4).
+-Futher primality testing with
++Further primality testing with
+ .Xr ssh-keygen 1
+ produces safe prime moduli (type 2) that are ready for use in
+ .Xr sshd 8 .
+@@ -85,7 +85,7 @@ Composite number - not prime.
+ .It 0x02
+ Sieve of Eratosthenes
+ .It 0x04
+-Probabalistic Miller-Rabin primality tests.
++Probabilistic Miller-Rabin primality tests.
+ .El
+ .Pp
+ The
+@@ -95,7 +95,7 @@ Subsequent
+ .Xr ssh-keygen 1
+ primality tests are Miller-Rabin tests (flag 0x04).
+ .It trials
+-Decimal number indicating of primaility trials that have been performed
++Decimal number indicating of primality trials that have been performed
+ on the modulus.
+ .It size
+ Decimal number indicating the size of the prime in bits.
+diff -up openssh-5.3p1/ssh-ldap.conf.5.man openssh-5.3p1/ssh-ldap.conf.5
+--- openssh-5.3p1/ssh-ldap.conf.5.man	2013-07-18 14:34:56.235699000 +0200
++++ openssh-5.3p1/ssh-ldap.conf.5	2013-07-18 14:36:33.506279196 +0200
+@@ -77,7 +77,7 @@ Specifies the default password to use wh
+ .Cm BindDN .
+ There is no default.
+ .It Cm RootBindDN
+-Intentionaly does nothing. Recognized for compatibility reasons.
++Intentionally does nothing. Recognized for compatibility reasons.
+ .It Cm Host
+ The argument(s) specifies the name(s) of an LDAP server(s) to which the
+ .Xr ssh-ldap-helper 8
+@@ -177,15 +177,15 @@ and
+ .Dq hard_init .
+ The value
+ .Dq hard
+-means that reconects that the
++means that reconnects that the
+ .Xr ssh-ldap-helper 8
+ tries to reconnect to the LDAP server 5 times before failure. There is exponential backoff before retrying.
+ The value
+ .Dq soft
+ means that
+ .Xr ssh-ldap-helper 8
+-fails immediately when it cannot connect to the LDAP seerver.
+-The deault is
++fails immediately when it cannot connect to the LDAP server.
++The default is
+ .Dq hard .
+ .It Cm SSLPath
+ Specifies the path to the X.509 certificate database.
+@@ -299,7 +299,7 @@ The value
+ .Dq hard
+ is the same as
+ .Dq demand .
+-It requires an SSL connection. In the case of the plain conection the
++It requires an SSL connection. In the case of the plain connection the
+ session is immediately terminated.
+ The default is
+ .Dq hard .
+@@ -356,7 +356,7 @@ There is no default.
+ Specifies the debug level used for logging by the LDAP client library.
+ There is no default.
+ .It Cm SSH_Filter
+-Specifies the user filter applied on the LDAP serch.
++Specifies the user filter applied on the LDAP search.
+ The default is no filter.
+ .El
+ .Sh FILES
diff --git a/openssh-5.3p1-gsissh.patch b/openssh-5.3p1-gsissh.patch
index 75116a0..c286e66 100644
--- a/openssh-5.3p1-gsissh.patch
+++ b/openssh-5.3p1-gsissh.patch
@@ -1,6 +1,6 @@
 diff -Nur openssh-5.3p1.orig/auth2.c openssh-5.3p1/auth2.c
---- openssh-5.3p1.orig/auth2.c	2012-08-14 08:39:57.180580380 +0200
-+++ openssh-5.3p1/auth2.c	2012-08-14 08:41:07.411697289 +0200
+--- openssh-5.3p1.orig/auth2.c	2013-11-26 19:13:13.528341954 +0100
++++ openssh-5.3p1/auth2.c	2013-11-26 19:13:58.944790679 +0100
 @@ -229,7 +229,27 @@
  	user = packet_get_string(NULL);
  	service = packet_get_string(NULL);
@@ -97,8 +97,8 @@ diff -Nur openssh-5.3p1.orig/auth2.c openssh-5.3p1/auth2.c
  		    authctxt->user, authctxt->service, user, service);
  	}
 diff -Nur openssh-5.3p1.orig/auth2-gss.c openssh-5.3p1/auth2-gss.c
---- openssh-5.3p1.orig/auth2-gss.c	2012-08-14 08:39:57.181580368 +0200
-+++ openssh-5.3p1/auth2-gss.c	2012-08-14 08:41:07.412697277 +0200
+--- openssh-5.3p1.orig/auth2-gss.c	2013-11-26 19:13:13.528341954 +0100
++++ openssh-5.3p1/auth2-gss.c	2013-11-26 19:13:58.944790679 +0100
 @@ -47,6 +47,7 @@
  
  extern ServerOptions options;
@@ -280,10 +280,10 @@ diff -Nur openssh-5.3p1.orig/auth2-gss.c openssh-5.3p1/auth2-gss.c
  	"gssapi-keyex",
  	userauth_gsskeyex,
 diff -Nur openssh-5.3p1.orig/auth.c openssh-5.3p1/auth.c
---- openssh-5.3p1.orig/auth.c	2012-08-14 08:39:57.182580356 +0200
-+++ openssh-5.3p1/auth.c	2012-08-14 08:41:07.436696971 +0200
-@@ -71,6 +71,9 @@
- #endif
+--- openssh-5.3p1.orig/auth.c	2013-11-26 19:13:13.568341468 +0100
++++ openssh-5.3p1/auth.c	2013-11-26 19:13:58.945790666 +0100
+@@ -72,6 +72,9 @@
+ #include "authfile.h"
  #include "monitor_wrap.h"
  
 +#include "version.h"
@@ -292,7 +292,7 @@ diff -Nur openssh-5.3p1.orig/auth.c openssh-5.3p1/auth.c
  /* import */
  extern ServerOptions options;
  extern int use_privsep;
-@@ -271,7 +274,8 @@
+@@ -272,7 +275,8 @@
  	    method,
  	    submethod == NULL ? "" : "/", submethod == NULL ? "" : submethod,
  	    authctxt->valid ? "" : "invalid user ",
@@ -302,7 +302,7 @@ diff -Nur openssh-5.3p1.orig/auth.c openssh-5.3p1/auth.c
  	    get_remote_ipaddr(),
  	    get_remote_port(),
  	    info);
-@@ -293,6 +297,21 @@
+@@ -294,6 +298,21 @@
  	if (authenticated == 0 && !authctxt->postponed)
  		audit_event(audit_classify_auth(method));
  #endif
@@ -324,7 +324,7 @@ diff -Nur openssh-5.3p1.orig/auth.c openssh-5.3p1/auth.c
  }
  
  /*
-@@ -327,7 +346,7 @@
+@@ -328,7 +347,7 @@
   *
   * This returns a buffer allocated by xmalloc.
   */
@@ -333,7 +333,7 @@ diff -Nur openssh-5.3p1.orig/auth.c openssh-5.3p1/auth.c
  expand_authorized_keys(const char *filename, struct passwd *pw)
  {
  	char *file, ret[MAXPATHLEN];
-@@ -528,9 +547,14 @@
+@@ -552,9 +571,14 @@
  	    get_canonical_hostname(options.use_dns), get_remote_ipaddr());
  
  	pw = getpwnam(user);
@@ -350,8 +350,8 @@ diff -Nur openssh-5.3p1.orig/auth.c openssh-5.3p1/auth.c
  		record_failed_login(user,
  		    get_canonical_hostname(options.use_dns), "ssh");
 diff -Nur openssh-5.3p1.orig/auth.h openssh-5.3p1/auth.h
---- openssh-5.3p1.orig/auth.h	2012-08-14 08:39:57.182580356 +0200
-+++ openssh-5.3p1/auth.h	2012-08-14 08:46:39.092526709 +0200
+--- openssh-5.3p1.orig/auth.h	2013-11-26 19:13:13.569341456 +0100
++++ openssh-5.3p1/auth.h	2013-11-26 19:13:58.945790666 +0100
 @@ -148,6 +148,7 @@
  void	auth_log(Authctxt *, int, const char *, const char *, const char *);
  void	userauth_finish(Authctxt *, int, const char *, const char *);
@@ -361,8 +361,8 @@ diff -Nur openssh-5.3p1.orig/auth.h openssh-5.3p1/auth.h
  void	userauth_send_banner(const char *);
  
 diff -Nur openssh-5.3p1.orig/auth-pam.c openssh-5.3p1/auth-pam.c
---- openssh-5.3p1.orig/auth-pam.c	2012-08-14 08:39:56.995582706 +0200
-+++ openssh-5.3p1/auth-pam.c	2012-08-14 08:41:07.478696447 +0200
+--- openssh-5.3p1.orig/auth-pam.c	2013-11-26 19:13:13.403343471 +0100
++++ openssh-5.3p1/auth-pam.c	2013-11-26 19:13:58.946790654 +0100
 @@ -122,6 +122,10 @@
   */
  typedef pthread_t sp_pthread_t;
@@ -426,7 +426,7 @@ diff -Nur openssh-5.3p1.orig/auth-pam.c openssh-5.3p1/auth-pam.c
  void
  sshpam_password_change_required(int reqd)
  {
-@@ -294,7 +341,7 @@
+@@ -294,7 +343,7 @@
  static void
  import_environments(Buffer *b)
  {
@@ -435,7 +435,7 @@ diff -Nur openssh-5.3p1.orig/auth-pam.c openssh-5.3p1/auth-pam.c
  	u_int i, num_env;
  	int err;
  
-@@ -304,6 +351,15 @@
+@@ -304,6 +353,15 @@
  	/* Import variables set by do_pam_account */
  	sshpam_account_status = buffer_get_int(b);
  	sshpam_password_change_required(buffer_get_int(b));
@@ -451,7 +451,7 @@ diff -Nur openssh-5.3p1.orig/auth-pam.c openssh-5.3p1/auth-pam.c
  
  	/* Import environment from subprocess */
  	num_env = buffer_get_int(b);
-@@ -469,6 +525,9 @@
+@@ -469,6 +527,9 @@
  	if (sshpam_err != PAM_SUCCESS)
  		goto auth_fail;
  
@@ -461,7 +461,7 @@ diff -Nur openssh-5.3p1.orig/auth-pam.c openssh-5.3p1/auth-pam.c
  	if (compat20) {
  		if (!do_pam_account()) {
  			sshpam_err = PAM_ACCT_EXPIRED;
-@@ -489,6 +548,9 @@
+@@ -489,6 +550,9 @@
  	/* Export variables set by do_pam_account */
  	buffer_put_int(&buffer, sshpam_account_status);
  	buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
@@ -471,7 +471,7 @@ diff -Nur openssh-5.3p1.orig/auth-pam.c openssh-5.3p1/auth-pam.c
  
  	/* Export any environment strings set in child */
  	for(i = 0; environ[i] != NULL; i++)
-@@ -907,6 +969,12 @@
+@@ -907,6 +971,12 @@
  	debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
  	    pam_strerror(sshpam_handle, sshpam_err));
  
@@ -484,7 +484,7 @@ diff -Nur openssh-5.3p1.orig/auth-pam.c openssh-5.3p1/auth-pam.c
  	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
  		sshpam_account_status = 0;
  		return (sshpam_account_status);
-@@ -1206,6 +1274,9 @@
+@@ -1206,6 +1276,9 @@
  		    pam_strerror(sshpam_handle, sshpam_err));
  
  	sshpam_err = pam_authenticate(sshpam_handle, flags);
@@ -495,8 +495,8 @@ diff -Nur openssh-5.3p1.orig/auth-pam.c openssh-5.3p1/auth-pam.c
  	if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
  		debug("PAM: password authentication accepted for %.100s",
 diff -Nur openssh-5.3p1.orig/auth-pam.h openssh-5.3p1/auth-pam.h
---- openssh-5.3p1.orig/auth-pam.h	2012-08-14 08:39:56.994582718 +0200
-+++ openssh-5.3p1/auth-pam.h	2012-08-14 08:41:07.478696447 +0200
+--- openssh-5.3p1.orig/auth-pam.h	2013-11-26 19:13:13.403343471 +0100
++++ openssh-5.3p1/auth-pam.h	2013-11-26 19:13:58.946790654 +0100
 @@ -46,5 +46,6 @@
  void sshpam_cleanup(void);
  int sshpam_auth_passwd(Authctxt *, const char *);
@@ -505,8 +505,8 @@ diff -Nur openssh-5.3p1.orig/auth-pam.h openssh-5.3p1/auth-pam.h
  
  #endif /* USE_PAM */
 diff -Nur openssh-5.3p1.orig/canohost.c openssh-5.3p1/canohost.c
---- openssh-5.3p1.orig/canohost.c	2012-08-14 08:39:57.019582404 +0200
-+++ openssh-5.3p1/canohost.c	2012-08-14 08:41:07.479696435 +0200
+--- openssh-5.3p1.orig/canohost.c	2013-11-26 19:13:13.419343277 +0100
++++ openssh-5.3p1/canohost.c	2013-11-26 19:13:58.946790654 +0100
 @@ -16,6 +16,7 @@
  
  #include <sys/types.h>
@@ -551,7 +551,7 @@ diff -Nur openssh-5.3p1.orig/canohost.c openssh-5.3p1/canohost.c
 +}
 diff -Nur openssh-5.3p1.orig/canohost.h openssh-5.3p1/canohost.h
 --- openssh-5.3p1.orig/canohost.h	2009-06-21 11:50:08.000000000 +0200
-+++ openssh-5.3p1/canohost.h	2012-08-14 08:41:07.479696435 +0200
++++ openssh-5.3p1/canohost.h	2013-11-26 19:13:58.947790642 +0100
 @@ -26,4 +26,6 @@
  int		 get_sock_port(int, int);
  void		 clear_cached_addr(void);
@@ -560,8 +560,8 @@ diff -Nur openssh-5.3p1.orig/canohost.h openssh-5.3p1/canohost.h
 +
  void		 ipv64_normalise_mapped(struct sockaddr_storage *, socklen_t *);
 diff -Nur openssh-5.3p1.orig/configure.ac openssh-5.3p1/configure.ac
---- openssh-5.3p1.orig/configure.ac	2012-08-14 08:39:57.175580443 +0200
-+++ openssh-5.3p1/configure.ac	2012-08-14 08:41:07.480696423 +0200
+--- openssh-5.3p1.orig/configure.ac	2013-11-26 19:13:13.548341711 +0100
++++ openssh-5.3p1/configure.ac	2013-11-26 19:13:58.948790630 +0100
 @@ -3643,6 +3643,14 @@
  			AC_CHECK_HEADER(gssapi_krb5.h, ,
  					[ CPPFLAGS="$oldCPP" ])
@@ -638,8 +638,8 @@ diff -Nur openssh-5.3p1.orig/configure.ac openssh-5.3p1/configure.ac
  	fi
  	])
 diff -Nur openssh-5.3p1.orig/gss-genr.c openssh-5.3p1/gss-genr.c
---- openssh-5.3p1.orig/gss-genr.c	2012-08-14 08:39:57.068581789 +0200
-+++ openssh-5.3p1/gss-genr.c	2012-08-14 08:41:07.481696410 +0200
+--- openssh-5.3p1.orig/gss-genr.c	2013-11-26 19:13:13.448342925 +0100
++++ openssh-5.3p1/gss-genr.c	2013-11-26 19:13:58.948790630 +0100
 @@ -38,6 +38,7 @@
  #include "xmalloc.h"
  #include "buffer.h"
@@ -677,9 +677,9 @@ diff -Nur openssh-5.3p1.orig/gss-genr.c openssh-5.3p1/gss-genr.c
  	return (ctx->major);
  }
 diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
---- openssh-5.3p1.orig/gss-serv.c	2012-08-14 08:39:57.191580243 +0200
-+++ openssh-5.3p1/gss-serv.c	2012-08-14 08:41:07.482696397 +0200
-@@ -52,6 +52,7 @@
+--- openssh-5.3p1.orig/gss-serv.c	2013-11-26 19:13:13.537341844 +0100
++++ openssh-5.3p1/gss-serv.c	2013-11-26 19:13:58.948790630 +0100
+@@ -52,10 +52,12 @@
  #include "monitor_wrap.h"
  
  extern ServerOptions options;
@@ -687,7 +687,13 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  
  static ssh_gssapi_client gssapi_client =
      { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
-@@ -63,14 +64,23 @@
+-    GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME,  NULL, {NULL, NULL, NULL}, 0, 0};
++      GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL},
++      GSS_C_NO_CONTEXT, 0, 0};
+ 
+ ssh_gssapi_mech gssapi_null_mech =
+     { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
+@@ -63,14 +65,23 @@
  #ifdef KRB5
  extern ssh_gssapi_mech gssapi_kerberos_mech;
  #endif
@@ -711,7 +717,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  
  /*
   * Acquire credentials for a server running on the current host.
-@@ -159,7 +169,8 @@
+@@ -159,7 +170,8 @@
  
  	gss_create_empty_oid_set(&min_status, oidset);
  
@@ -721,7 +727,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  		return;
  
  	while (supported_mechs[i]->name != NULL) {
-@@ -212,6 +223,10 @@
+@@ -212,6 +224,10 @@
  	    (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
  		if (ssh_gssapi_getclient(ctx, &gssapi_client))
  			fatal("Couldn't convert client name");
@@ -732,7 +738,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  	}
  
  	return (status);
-@@ -231,6 +246,17 @@
+@@ -231,6 +247,17 @@
  
  	tok = ename->value;
  
@@ -750,7 +756,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  	/*
  	 * Check that ename is long enough for all of the fixed length
  	 * header, and that the initial ID bytes are correct
-@@ -298,8 +324,11 @@
+@@ -298,8 +325,11 @@
  			return GSS_S_COMPLETE;
  		}
  
@@ -764,7 +770,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  		    NULL, NULL, NULL))) {
  			ssh_gssapi_error(ctx);
  			return (ctx->major);
-@@ -342,9 +371,12 @@
+@@ -342,9 +372,12 @@
  	if (client->mech == NULL)
  		return GSS_S_FAILURE;
  
@@ -779,7 +785,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  		ssh_gssapi_error(ctx);
  		return (ctx->major);
  	}
-@@ -371,6 +403,10 @@
+@@ -371,6 +404,10 @@
  	/* We can't copy this structure, so we just move the pointer to it */
  	client->creds = ctx->client_creds;
  	ctx->client_creds = GSS_C_NO_CREDENTIAL;
@@ -790,7 +796,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  	return (ctx->major);
  }
  
-@@ -391,6 +427,11 @@
+@@ -391,6 +428,11 @@
  ssh_gssapi_storecreds(void)
  {
  	if (gssapi_client.mech && gssapi_client.mech->storecreds) {
@@ -802,7 +808,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  		(*gssapi_client.mech->storecreds)(&gssapi_client);
  	} else
  		debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
-@@ -415,7 +456,7 @@
+@@ -415,7 +457,7 @@
  
  /* Privileged */
  int
@@ -811,7 +817,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  {
  	OM_uint32 lmin;
  
-@@ -424,6 +465,12 @@
+@@ -424,6 +466,12 @@
  		debug("No suitable client data");
  		return 0;
  	}
@@ -824,7 +830,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  	if (gssapi_client.mech && gssapi_client.mech->userok)
  		if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
  			gssapi_client.used = 1;
-@@ -442,6 +489,24 @@
+@@ -442,6 +490,24 @@
  	return (0);
  }
  
@@ -849,7 +855,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  /* These bits are only used for rekeying. The unpriviledged child is running 
   * as the user, the monitor is root.
   *
-@@ -468,6 +533,7 @@
+@@ -468,6 +534,7 @@
  	pam_handle_t *pamh = NULL;
  	struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
  	char *envstr;
@@ -857,7 +863,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  #endif
  
  	if (gssapi_client.store.filename == NULL && 
-@@ -497,6 +563,18 @@
+@@ -497,6 +564,18 @@
  	if (ret)
  		return;
  
@@ -876,7 +882,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  	xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, 
  	    gssapi_client.store.envval);
  
-@@ -528,4 +606,12 @@
+@@ -528,4 +607,12 @@
  	return ok;
  }
  
@@ -891,7 +897,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv.c openssh-5.3p1/gss-serv.c
  #endif
 diff -Nur openssh-5.3p1.orig/gss-serv-gsi.c openssh-5.3p1/gss-serv-gsi.c
 --- openssh-5.3p1.orig/gss-serv-gsi.c	1970-01-01 01:00:00.000000000 +0100
-+++ openssh-5.3p1/gss-serv-gsi.c	2012-08-14 08:41:07.482696397 +0200
++++ openssh-5.3p1/gss-serv-gsi.c	2013-11-26 19:13:58.949790618 +0100
 @@ -0,0 +1,238 @@
 +/*
 + * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
@@ -1132,9 +1138,9 @@ diff -Nur openssh-5.3p1.orig/gss-serv-gsi.c openssh-5.3p1/gss-serv-gsi.c
 +#endif /* GSI */
 +#endif /* GSSAPI */
 diff -Nur openssh-5.3p1.orig/gss-serv-krb5.c openssh-5.3p1/gss-serv-krb5.c
---- openssh-5.3p1.orig/gss-serv-krb5.c	2012-08-14 08:39:57.126581059 +0200
-+++ openssh-5.3p1/gss-serv-krb5.c	2012-08-14 08:41:07.483696384 +0200
-@@ -109,7 +109,35 @@
+--- openssh-5.3p1.orig/gss-serv-krb5.c	2013-11-26 19:13:13.590341201 +0100
++++ openssh-5.3p1/gss-serv-krb5.c	2013-11-26 19:13:58.949790618 +0100
+@@ -110,7 +110,35 @@
  	return retval;
  }
  
@@ -1170,7 +1176,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv-krb5.c openssh-5.3p1/gss-serv-krb5.c
  /* This writes out any forwarded credentials from the structure populated
   * during userauth. Called after we have setuid to the user */
  
-@@ -191,7 +219,7 @@
+@@ -199,7 +227,7 @@
  	return;
  }
  
@@ -1179,7 +1185,7 @@ diff -Nur openssh-5.3p1.orig/gss-serv-krb5.c openssh-5.3p1/gss-serv-krb5.c
  ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, 
      ssh_gssapi_client *client)
  {
-@@ -262,7 +290,7 @@
+@@ -270,7 +298,7 @@
  	{9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"},
  	NULL,
  	&ssh_gssapi_krb5_userok,
@@ -1189,8 +1195,8 @@ diff -Nur openssh-5.3p1.orig/gss-serv-krb5.c openssh-5.3p1/gss-serv-krb5.c
  	&ssh_gssapi_krb5_updatecreds
  };
 diff -Nur openssh-5.3p1.orig/kexgsss.c openssh-5.3p1/kexgsss.c
---- openssh-5.3p1.orig/kexgsss.c	2012-08-14 08:39:57.071581750 +0200
-+++ openssh-5.3p1/kexgsss.c	2012-08-14 08:41:07.483696384 +0200
+--- openssh-5.3p1.orig/kexgsss.c	2013-11-26 19:13:13.450342900 +0100
++++ openssh-5.3p1/kexgsss.c	2013-11-26 19:13:58.949790618 +0100
 @@ -44,6 +44,7 @@
  #include "monitor_wrap.h"
  #include "servconf.h"
@@ -1252,7 +1258,7 @@ diff -Nur openssh-5.3p1.orig/kexgsss.c openssh-5.3p1/kexgsss.c
  #endif /* GSSAPI */
 diff -Nur openssh-5.3p1.orig/LICENSE.globus_usage openssh-5.3p1/LICENSE.globus_usage
 --- openssh-5.3p1.orig/LICENSE.globus_usage	1970-01-01 01:00:00.000000000 +0100
-+++ openssh-5.3p1/LICENSE.globus_usage	2012-08-14 08:41:07.483696384 +0200
++++ openssh-5.3p1/LICENSE.globus_usage	2013-11-26 19:13:58.949790618 +0100
 @@ -0,0 +1,18 @@
 +/*
 + * Portions of the Usage Metrics suport code are derived from the
@@ -1273,9 +1279,9 @@ diff -Nur openssh-5.3p1.orig/LICENSE.globus_usage openssh-5.3p1/LICENSE.globus_u
 + * limitations under the License.
 + */
 diff -Nur openssh-5.3p1.orig/Makefile.in openssh-5.3p1/Makefile.in
---- openssh-5.3p1.orig/Makefile.in	2012-08-14 08:39:57.199580142 +0200
-+++ openssh-5.3p1/Makefile.in	2012-08-14 08:41:07.484696371 +0200
-@@ -91,8 +91,10 @@
+--- openssh-5.3p1.orig/Makefile.in	2013-11-26 19:13:13.585341262 +0100
++++ openssh-5.3p1/Makefile.in	2013-11-26 19:47:08.349685587 +0100
+@@ -93,8 +93,10 @@
  	monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \
  	auth-krb5.o audit-linux.o \
   	auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o\
@@ -1287,8 +1293,8 @@ diff -Nur openssh-5.3p1.orig/Makefile.in openssh-5.3p1/Makefile.in
  
  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-rand-helper.8.out ssh-keysign.8.out ssh-ldap-helper.8.out sshd_config.5.out ssh_config.5.out ssh-ldap.conf.5.out
 diff -Nur openssh-5.3p1.orig/misc.c openssh-5.3p1/misc.c
---- openssh-5.3p1.orig/misc.c	2012-08-14 08:39:56.989582782 +0200
-+++ openssh-5.3p1/misc.c	2012-08-14 08:41:07.484696371 +0200
+--- openssh-5.3p1.orig/misc.c	2013-11-26 19:13:13.400343507 +0100
++++ openssh-5.3p1/misc.c	2013-11-26 19:13:58.950790606 +0100
 @@ -155,11 +155,14 @@
  #define WHITESPACE " \t\r\n"
  #define QUOTE	"\""
@@ -1349,8 +1355,8 @@ diff -Nur openssh-5.3p1.orig/misc.c openssh-5.3p1/misc.c
   * Convert ASCII string to TCP/IP port number.
   * Port must be >=0 and <=65535.
 diff -Nur openssh-5.3p1.orig/misc.h openssh-5.3p1/misc.h
---- openssh-5.3p1.orig/misc.h	2008-06-12 22:42:45.000000000 +0200
-+++ openssh-5.3p1/misc.h	2012-08-14 08:41:07.485696359 +0200
+--- openssh-5.3p1.orig/misc.h	2013-11-26 19:13:13.585341262 +0100
++++ openssh-5.3p1/misc.h	2013-11-26 19:13:58.950790606 +0100
 @@ -37,6 +37,7 @@
  void	 ms_to_timeval(struct timeval *, int);
  
@@ -1360,8 +1366,8 @@ diff -Nur openssh-5.3p1.orig/misc.h openssh-5.3p1/misc.h
  
  typedef struct arglist arglist;
 diff -Nur openssh-5.3p1.orig/monitor.c openssh-5.3p1/monitor.c
---- openssh-5.3p1.orig/monitor.c	2012-08-14 08:39:57.183580343 +0200
-+++ openssh-5.3p1/monitor.c	2012-08-14 08:53:21.123477167 +0200
+--- openssh-5.3p1.orig/monitor.c	2013-11-26 19:13:13.574341395 +0100
++++ openssh-5.3p1/monitor.c	2013-11-26 19:13:58.951790594 +0100
 @@ -179,6 +179,9 @@
  int mm_answer_gss_userok(int, Buffer *);
  int mm_answer_gss_checkmic(int, Buffer *);
@@ -1457,7 +1463,7 @@ diff -Nur openssh-5.3p1.orig/monitor.c openssh-5.3p1/monitor.c
  	setproctitle("%s [priv]", pwent ? username : "unknown");
  	xfree(username);
  
-@@ -2179,12 +2194,15 @@
+@@ -2184,12 +2199,15 @@
  mm_answer_gss_userok(int sock, Buffer *m)
  {
  	int authenticated;
@@ -1474,7 +1480,7 @@ diff -Nur openssh-5.3p1.orig/monitor.c openssh-5.3p1/monitor.c
  
  	buffer_clear(m);
  	buffer_put_int(m, authenticated);
-@@ -2192,13 +2210,78 @@
+@@ -2197,13 +2215,78 @@
  	debug3("%s: sending result %d", __func__, authenticated);
  	mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
  
@@ -1555,8 +1561,8 @@ diff -Nur openssh-5.3p1.orig/monitor.c openssh-5.3p1/monitor.c
  mm_answer_gss_sign(int socket, Buffer *m)
  {
 diff -Nur openssh-5.3p1.orig/monitor.h openssh-5.3p1/monitor.h
---- openssh-5.3p1.orig/monitor.h	2012-08-14 08:39:57.114581209 +0200
-+++ openssh-5.3p1/monitor.h	2012-08-14 08:41:07.487696335 +0200
+--- openssh-5.3p1.orig/monitor.h	2013-11-26 19:13:13.483342500 +0100
++++ openssh-5.3p1/monitor.h	2013-11-26 19:13:58.951790594 +0100
 @@ -55,6 +55,9 @@
  	MONITOR_REQ_GSSSETUP, MONITOR_ANS_GSSSETUP,
  	MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP,
@@ -1568,9 +1574,9 @@ diff -Nur openssh-5.3p1.orig/monitor.h openssh-5.3p1/monitor.h
  	MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN,
  	MONITOR_REQ_GSSUPCREDS, MONITOR_ANS_GSSUPCREDS,
 diff -Nur openssh-5.3p1.orig/monitor_wrap.c openssh-5.3p1/monitor_wrap.c
---- openssh-5.3p1.orig/monitor_wrap.c	2012-08-14 08:39:57.115581197 +0200
-+++ openssh-5.3p1/monitor_wrap.c	2012-08-14 08:41:07.487696335 +0200
-@@ -1340,12 +1340,13 @@
+--- openssh-5.3p1.orig/monitor_wrap.c	2013-11-26 19:13:13.530341929 +0100
++++ openssh-5.3p1/monitor_wrap.c	2013-11-26 19:13:58.951790594 +0100
+@@ -1344,12 +1344,13 @@
  }
  
  int
@@ -1585,7 +1591,7 @@ diff -Nur openssh-5.3p1.orig/monitor_wrap.c openssh-5.3p1/monitor_wrap.c
  
  	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m);
  	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK,
-@@ -1358,6 +1359,83 @@
+@@ -1362,6 +1363,83 @@
  	return (authenticated);
  }
  
@@ -1670,8 +1676,8 @@ diff -Nur openssh-5.3p1.orig/monitor_wrap.c openssh-5.3p1/monitor_wrap.c
  mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
  {
 diff -Nur openssh-5.3p1.orig/monitor_wrap.h openssh-5.3p1/monitor_wrap.h
---- openssh-5.3p1.orig/monitor_wrap.h	2012-08-14 08:39:57.115581197 +0200
-+++ openssh-5.3p1/monitor_wrap.h	2012-08-14 08:41:07.488696322 +0200
+--- openssh-5.3p1.orig/monitor_wrap.h	2013-11-26 19:13:13.484342488 +0100
++++ openssh-5.3p1/monitor_wrap.h	2013-11-26 19:13:58.951790594 +0100
 @@ -61,9 +61,13 @@
  OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
  OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
@@ -1688,9 +1694,9 @@ diff -Nur openssh-5.3p1.orig/monitor_wrap.h openssh-5.3p1/monitor_wrap.h
  #endif
  
 diff -Nur openssh-5.3p1.orig/readconf.c openssh-5.3p1/readconf.c
---- openssh-5.3p1.orig/readconf.c	2012-08-14 08:39:57.075581701 +0200
-+++ openssh-5.3p1/readconf.c	2012-08-14 08:41:07.488696322 +0200
-@@ -1168,13 +1168,13 @@
+--- openssh-5.3p1.orig/readconf.c	2013-11-26 19:13:13.557341602 +0100
++++ openssh-5.3p1/readconf.c	2013-11-26 19:13:58.952790581 +0100
+@@ -1185,13 +1185,13 @@
  	if (options->challenge_response_authentication == -1)
  		options->challenge_response_authentication = 1;
  	if (options->gss_authentication == -1)
@@ -1709,9 +1715,9 @@ diff -Nur openssh-5.3p1.orig/readconf.c openssh-5.3p1/readconf.c
  		options->gss_renewal_rekey = 0;
  	if (options->password_authentication == -1)
 diff -Nur openssh-5.3p1.orig/readconf.h openssh-5.3p1/readconf.h
---- openssh-5.3p1.orig/readconf.h	2012-08-14 08:39:57.075581701 +0200
-+++ openssh-5.3p1/readconf.h	2012-08-14 08:41:07.489696309 +0200
-@@ -80,6 +80,8 @@
+--- openssh-5.3p1.orig/readconf.h	2013-11-26 19:13:13.558341589 +0100
++++ openssh-5.3p1/readconf.h	2013-11-26 19:13:58.952790581 +0100
+@@ -81,6 +81,8 @@
  	char   *host_key_alias;	/* hostname alias for .ssh/known_hosts */
  	char   *proxy_command;	/* Proxy command for connecting the host. */
  	char   *user;		/* User to log in as. */
@@ -1721,8 +1727,8 @@ diff -Nur openssh-5.3p1.orig/readconf.h openssh-5.3p1/readconf.h
  
  	char   *system_hostfile;/* Path for /etc/ssh/ssh_known_hosts. */
 diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
---- openssh-5.3p1.orig/servconf.c	2012-08-14 08:39:57.184580330 +0200
-+++ openssh-5.3p1/servconf.c	2012-08-14 08:47:45.684690246 +0200
+--- openssh-5.3p1.orig/servconf.c	2013-11-26 19:13:13.592341177 +0100
++++ openssh-5.3p1/servconf.c	2013-11-26 19:13:58.953790569 +0100
 @@ -60,6 +60,7 @@
  
  	/* Portable-specific options */
@@ -1731,7 +1737,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  
  	/* Standard Options */
  	options->num_ports = 0;
-@@ -94,9 +95,11 @@
+@@ -95,9 +96,11 @@
  	options->kerberos_ticket_cleanup = -1;
  	options->kerberos_get_afs_token = -1;
  	options->gss_authentication=-1;
@@ -1743,16 +1749,16 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	options->gss_store_rekey = -1;
  	options->password_authentication = -1;
  	options->kbd_interactive_authentication = -1;
-@@ -139,6 +142,8 @@
- 	options->authorized_keys_command_runas = NULL;
- 	options->zero_knowledge_password_authentication = -1;
+@@ -144,6 +147,8 @@
+ 	options->trusted_user_ca_keys = NULL;
+ 	options->authorized_principals_file = NULL;
  	options->use_kuserok = -1;
 +	options->disable_usage_stats = 0;
 +	options->usage_stats_targets = NULL;
  }
  
  void
-@@ -147,6 +152,8 @@
+@@ -152,6 +157,8 @@
  	/* Portable-specific options */
  	if (options->use_pam == -1)
  		options->use_pam = 0;
@@ -1761,7 +1767,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  
  	/* Standard Options */
  	if (options->protocol == SSH_PROTO_UNKNOWN)
-@@ -220,13 +227,17 @@
+@@ -226,13 +233,17 @@
  	if (options->kerberos_get_afs_token == -1)
  		options->kerberos_get_afs_token = 0;
  	if (options->gss_authentication == -1)
@@ -1781,7 +1787,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	if (options->gss_store_rekey == -1)
  		options->gss_store_rekey = 0;
  	if (options->password_authentication == -1)
-@@ -303,7 +314,7 @@
+@@ -309,7 +320,7 @@
  typedef enum {
  	sBadOption,		/* == unknown option */
  	/* Portable-specific options */
@@ -1790,7 +1796,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	/* Standard Options */
  	sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime,
  	sPermitRootLogin, sLogFacility, sLogLevel,
-@@ -324,12 +335,16 @@
+@@ -330,12 +341,16 @@
  	sBanner, sShowPatchLevel, sUseDNS, sHostbasedAuthentication,
  	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
  	sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
@@ -1804,10 +1810,10 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
  	sUsePrivilegeSeparation, sAllowAgentForwarding,
 +	sDisUsageStats, sUsageStatsTarg,
- 	sZeroKnowledgePasswordAuthentication,
+ 	sZeroKnowledgePasswordAuthentication, sHostCertificate,
+ 	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
  	sAuthorizedKeysCommand, sAuthorizedKeysCommandRunAs,
- 	sDeprecated, sUnsupported
-@@ -348,8 +363,10 @@
+@@ -356,8 +371,10 @@
  	/* Portable-specific options */
  #ifdef USE_PAM
  	{ "usepam", sUsePAM, SSHCFG_GLOBAL },
@@ -1818,7 +1824,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  #endif
  	{ "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL },
  	/* Standard Options */
-@@ -391,13 +408,23 @@
+@@ -399,13 +416,23 @@
  	{ "afstokenpassing", sUnsupported, SSHCFG_GLOBAL },
  #ifdef GSSAPI
  	{ "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
@@ -1842,16 +1848,16 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	{ "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
  	{ "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
  	{ "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
-@@ -459,6 +486,8 @@
- 	{ "permitopen", sPermitOpen, SSHCFG_ALL },
- 	{ "forcecommand", sForceCommand, SSHCFG_ALL },
- 	{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
+@@ -471,6 +498,8 @@
+ 	{ "revokedkeys", sRevokedKeys, SSHCFG_ALL },
+ 	{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
+ 	{ "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_GLOBAL },
 +	{ "disableusagestats", sDisUsageStats, SSHCFG_GLOBAL},
 +	{ "usagestatstargets", sUsageStatsTarg, SSHCFG_GLOBAL},
  #ifdef WITH_AUTHORIZED_KEYS_COMMAND
  	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
  	{ "authorizedkeyscommandrunas", sAuthorizedKeysCommandRunAs, SSHCFG_ALL },
-@@ -725,6 +754,10 @@
+@@ -754,6 +783,10 @@
  		intptr = &options->use_pam;
  		goto parse_flag;
  
@@ -1862,7 +1868,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	/* Standard Options */
  	case sBadOption:
  		return -1;
-@@ -935,6 +968,10 @@
+@@ -974,6 +1007,10 @@
  		intptr = &options->gss_authentication;
  		goto parse_flag;
  
@@ -1873,7 +1879,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	case sGssKeyEx:
  		intptr = &options->gss_keyex;
  		goto parse_flag;
-@@ -943,6 +980,10 @@
+@@ -982,6 +1019,10 @@
  		intptr = &options->gss_cleanup_creds;
  		goto parse_flag;
  
@@ -1884,7 +1890,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	case sGssStrictAcceptor:
  		intptr = &options->gss_strict_acceptor;
  		goto parse_flag;
-@@ -951,6 +992,12 @@
+@@ -990,6 +1031,12 @@
  		intptr = &options->gss_store_rekey;
  		goto parse_flag;
  
@@ -1897,7 +1903,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	case sPasswordAuthentication:
  		intptr = &options->password_authentication;
  		goto parse_flag;
-@@ -1395,6 +1442,18 @@
+@@ -1454,6 +1501,18 @@
  			*charptr = xstrdup(arg);
  		break;
  
@@ -1916,7 +1922,7 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	case sAuthorizedKeysCommand:
  		len = strspn(cp, WHITESPACE);
  		if (*activep && options->authorized_keys_command == NULL)
-@@ -1500,6 +1559,7 @@
+@@ -1567,6 +1626,7 @@
  {
  	M_CP_INTOPT(password_authentication);
  	M_CP_INTOPT(gss_authentication);
@@ -1925,9 +1931,9 @@ diff -Nur openssh-5.3p1.orig/servconf.c openssh-5.3p1/servconf.c
  	M_CP_INTOPT(pubkey_authentication);
  	M_CP_STROPT(authorized_keys_command);
 diff -Nur openssh-5.3p1.orig/servconf.h openssh-5.3p1/servconf.h
---- openssh-5.3p1.orig/servconf.h	2012-08-14 08:39:57.184580330 +0200
-+++ openssh-5.3p1/servconf.h	2012-08-14 08:41:07.491696283 +0200
-@@ -90,9 +90,12 @@
+--- openssh-5.3p1.orig/servconf.h	2013-11-26 19:13:13.577341359 +0100
++++ openssh-5.3p1/servconf.h	2013-11-26 19:13:58.953790569 +0100
+@@ -94,9 +94,12 @@
  						 * file on logout. */
  	int     kerberos_get_afs_token;		/* If true, try to get AFS token if
  						 * authenticated with Kerberos. */
@@ -1940,7 +1946,7 @@ diff -Nur openssh-5.3p1.orig/servconf.h openssh-5.3p1/servconf.h
  	int 	gss_strict_acceptor;	/* If true, restrict the GSSAPI acceptor name */
  	int 	gss_store_rekey;
  	int     password_authentication;	/* If true, permit password
-@@ -152,6 +155,7 @@
+@@ -156,6 +159,7 @@
  	char   *adm_forced_command;
  
  	int	use_pam;		/* Enable auth via PAM */
@@ -1948,10 +1954,10 @@ diff -Nur openssh-5.3p1.orig/servconf.h openssh-5.3p1/servconf.h
  
  	int	permit_tun;
  
-@@ -159,6 +163,10 @@
- 	int	use_kuserok;
- 
- 	char   *chroot_directory;
+@@ -166,6 +170,10 @@
+ 	char   *revoked_keys_file;
+ 	char   *trusted_user_ca_keys;
+ 	char   *authorized_principals_file;
 +
 +	int	disable_usage_stats;
 +	char   *usage_stats_targets;
@@ -1960,9 +1966,9 @@ diff -Nur openssh-5.3p1.orig/servconf.h openssh-5.3p1/servconf.h
  	char   *authorized_keys_command_runas;
  }       ServerOptions;
 diff -Nur openssh-5.3p1.orig/ssh.1 openssh-5.3p1/ssh.1
---- openssh-5.3p1.orig/ssh.1	2012-08-14 08:39:57.166580557 +0200
-+++ openssh-5.3p1/ssh.1	2012-08-14 08:41:07.492696271 +0200
-@@ -1238,6 +1238,18 @@
+--- openssh-5.3p1.orig/ssh.1	2013-11-26 19:13:13.577341359 +0100
++++ openssh-5.3p1/ssh.1	2013-11-26 19:13:58.953790569 +0100
+@@ -1270,6 +1270,18 @@
  on to new connections).
  .It Ev USER
  Set to the name of the user logging in.
@@ -1982,9 +1988,9 @@ diff -Nur openssh-5.3p1.orig/ssh.1 openssh-5.3p1/ssh.1
  .Pp
  Additionally,
 diff -Nur openssh-5.3p1.orig/ssh.c openssh-5.3p1/ssh.c
---- openssh-5.3p1.orig/ssh.c	2012-08-14 08:39:57.057581927 +0200
-+++ openssh-5.3p1/ssh.c	2012-08-14 08:41:07.493696259 +0200
-@@ -627,6 +627,32 @@
+--- openssh-5.3p1.orig/ssh.c	2013-11-26 19:13:13.583341286 +0100
++++ openssh-5.3p1/ssh.c	2013-11-26 19:13:58.954790557 +0100
+@@ -654,6 +654,32 @@
  			fatal("Can't open user config file %.100s: "
  			    "%.100s", config, strerror(errno));
  	} else {
@@ -2017,7 +2023,7 @@ diff -Nur openssh-5.3p1.orig/ssh.c openssh-5.3p1/ssh.c
  		r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir,
  		    _PATH_SSH_USER_CONFFILE);
  		if (r > 0 && (size_t)r < sizeof(buf))
-@@ -651,8 +677,12 @@
+@@ -678,8 +704,12 @@
  		logit("FIPS mode initialized");
  	}
  
@@ -2032,8 +2038,8 @@ diff -Nur openssh-5.3p1.orig/ssh.c openssh-5.3p1/ssh.c
  	/* Get default port if port has not been set. */
  	if (options.port == 0) {
 diff -Nur openssh-5.3p1.orig/ssh_config openssh-5.3p1/ssh_config
---- openssh-5.3p1.orig/ssh_config	2012-08-14 08:39:57.077581676 +0200
-+++ openssh-5.3p1/ssh_config	2012-08-14 08:41:07.493696259 +0200
+--- openssh-5.3p1.orig/ssh_config	2013-11-26 19:13:13.454342852 +0100
++++ openssh-5.3p1/ssh_config	2013-11-26 19:13:58.954790557 +0100
 @@ -24,10 +24,10 @@
  #   RSAAuthentication yes
  #   PasswordAuthentication yes
@@ -2050,8 +2056,8 @@ diff -Nur openssh-5.3p1.orig/ssh_config openssh-5.3p1/ssh_config
  #   CheckHostIP yes
  #   AddressFamily any
 diff -Nur openssh-5.3p1.orig/ssh_config.5 openssh-5.3p1/ssh_config.5
---- openssh-5.3p1.orig/ssh_config.5	2012-08-14 08:39:57.077581676 +0200
-+++ openssh-5.3p1/ssh_config.5	2012-08-14 08:41:07.494696247 +0200
+--- openssh-5.3p1.orig/ssh_config.5	2013-11-26 19:13:13.578341347 +0100
++++ openssh-5.3p1/ssh_config.5	2013-11-26 19:13:58.954790557 +0100
 @@ -56,6 +56,12 @@
  user's configuration file
  .Pq Pa ~/.ssh/config
@@ -2066,9 +2072,9 @@ diff -Nur openssh-5.3p1.orig/ssh_config.5 openssh-5.3p1/ssh_config.5
  .Pq Pa /etc/ssh/ssh_config
  .El
 diff -Nur openssh-5.3p1.orig/sshconnect2.c openssh-5.3p1/sshconnect2.c
---- openssh-5.3p1.orig/sshconnect2.c	2012-08-14 08:39:57.190580255 +0200
-+++ openssh-5.3p1/sshconnect2.c	2012-08-14 08:41:07.495696234 +0200
-@@ -615,6 +615,11 @@
+--- openssh-5.3p1.orig/sshconnect2.c	2013-11-26 19:13:13.586341250 +0100
++++ openssh-5.3p1/sshconnect2.c	2013-11-26 19:13:58.955790545 +0100
+@@ -650,6 +650,11 @@
  	const char* canonicalhost = get_canonical_hostname(1);
  	const char *gss_host;
  
@@ -2080,7 +2086,7 @@ diff -Nur openssh-5.3p1.orig/sshconnect2.c openssh-5.3p1/sshconnect2.c
  	if ( strcmp( canonicalhost, "UNKNOWN" )  == 0 )
  		remotehost = authctxt->host;
  	else
-@@ -850,6 +855,15 @@
+@@ -885,6 +890,15 @@
  	xfree(lang);
  }
  
@@ -2096,7 +2102,7 @@ diff -Nur openssh-5.3p1.orig/sshconnect2.c openssh-5.3p1/sshconnect2.c
  int
  userauth_gsskeyex(Authctxt *authctxt)
  {
-@@ -867,8 +881,16 @@
+@@ -902,8 +916,16 @@
  		return (0);
  	}
  
@@ -2113,7 +2119,7 @@ diff -Nur openssh-5.3p1.orig/sshconnect2.c openssh-5.3p1/sshconnect2.c
  
  	gssbuf.value = buffer_ptr(&b);
  	gssbuf.length = buffer_len(&b);
-@@ -879,7 +901,15 @@
+@@ -914,7 +936,15 @@
  	}
  
  	packet_start(SSH2_MSG_USERAUTH_REQUEST);
@@ -2130,11 +2136,11 @@ diff -Nur openssh-5.3p1.orig/sshconnect2.c openssh-5.3p1/sshconnect2.c
  	packet_put_cstring(authctxt->method->name);
  	packet_put_string(mic.value, mic.length);
 diff -Nur openssh-5.3p1.orig/sshd.8 openssh-5.3p1/sshd.8
---- openssh-5.3p1.orig/sshd.8	2012-08-14 08:39:57.166580557 +0200
-+++ openssh-5.3p1/sshd.8	2012-08-14 08:41:07.495696234 +0200
-@@ -682,6 +682,44 @@
- |1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa
- AAAA1234.....=
+--- openssh-5.3p1.orig/sshd.8	2013-11-26 19:13:13.579341335 +0100
++++ openssh-5.3p1/sshd.8	2013-11-26 19:13:58.955790545 +0100
+@@ -751,6 +751,44 @@
+ # A CA key, accepted for any host in *.mydomain.com or *.mydomain.org
+ @cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W...
  .Ed
 +.Sh ENVIRONMENT
 +.Nm
@@ -2178,8 +2184,8 @@ diff -Nur openssh-5.3p1.orig/sshd.8 openssh-5.3p1/sshd.8
  .Bl -tag -width Ds -compact
  .It ~/.hushlogin
 diff -Nur openssh-5.3p1.orig/sshd.c openssh-5.3p1/sshd.c
---- openssh-5.3p1.orig/sshd.c	2012-08-14 08:39:57.177580417 +0200
-+++ openssh-5.3p1/sshd.c	2012-08-14 08:41:07.496696221 +0200
+--- openssh-5.3p1.orig/sshd.c	2013-11-26 19:13:13.590341201 +0100
++++ openssh-5.3p1/sshd.c	2013-11-26 19:13:58.955790545 +0100
 @@ -122,6 +122,7 @@
  #include "roaming.h"
  #include "audit.h"
@@ -2188,7 +2194,7 @@ diff -Nur openssh-5.3p1.orig/sshd.c openssh-5.3p1/sshd.c
  
  #ifdef LIBWRAP
  #include <tcpd.h>
-@@ -1543,6 +1544,13 @@
+@@ -1606,6 +1607,13 @@
  	/* Fill in default values for those options not explicitly set. */
  	fill_default_server_options(&options);
  
@@ -2202,7 +2208,7 @@ diff -Nur openssh-5.3p1.orig/sshd.c openssh-5.3p1/sshd.c
  	/* challenge-response is implemented via keyboard interactive */
  	if (options.challenge_response_authentication)
  		options.kbd_interactive_authentication = 1;
-@@ -2033,7 +2041,7 @@
+@@ -2136,7 +2144,7 @@
  #endif
  
  #ifdef GSSAPI
@@ -2212,8 +2218,8 @@ diff -Nur openssh-5.3p1.orig/sshd.c openssh-5.3p1/sshd.c
  		ssh_gssapi_storecreds();
  		restore_uid();
 diff -Nur openssh-5.3p1.orig/sshd_config openssh-5.3p1/sshd_config
---- openssh-5.3p1.orig/sshd_config	2012-08-14 08:39:57.128581033 +0200
-+++ openssh-5.3p1/sshd_config	2012-08-14 08:41:07.497696208 +0200
+--- openssh-5.3p1.orig/sshd_config	2013-11-26 19:13:13.593341165 +0100
++++ openssh-5.3p1/sshd_config	2013-11-26 19:13:58.956790533 +0100
 @@ -77,12 +77,11 @@
  #KerberosUseKuserok yes
  
@@ -2250,9 +2256,9 @@ diff -Nur openssh-5.3p1.orig/sshd_config openssh-5.3p1/sshd_config
 +#UsageStatsTargets usage-stats.cilogon.org:4810
 +#DisableUsageStats no
 diff -Nur openssh-5.3p1.orig/sshd_config.5 openssh-5.3p1/sshd_config.5
---- openssh-5.3p1.orig/sshd_config.5	2012-08-14 08:39:57.185580317 +0200
-+++ openssh-5.3p1/sshd_config.5	2012-08-14 08:41:07.498696195 +0200
-@@ -335,6 +335,15 @@
+--- openssh-5.3p1.orig/sshd_config.5	2013-11-26 19:13:13.593341165 +0100
++++ openssh-5.3p1/sshd_config.5	2013-11-26 19:13:58.956790533 +0100
+@@ -371,6 +371,15 @@
  in
  .Xr ssh_config 5
  for more information on patterns.
@@ -2268,7 +2274,7 @@ diff -Nur openssh-5.3p1.orig/sshd_config.5 openssh-5.3p1/sshd_config.5
  .It Cm ForceCommand
  Forces the execution of the command specified by
  .Cm ForceCommand ,
-@@ -379,6 +388,10 @@
+@@ -415,6 +424,10 @@
  The default is
  .Dq no .
  Note that this option applies to protocol version 2 only.
@@ -2279,7 +2285,7 @@ diff -Nur openssh-5.3p1.orig/sshd_config.5 openssh-5.3p1/sshd_config.5
  .It Cm GSSAPIKeyExchange
  Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange
  doesn't rely on ssh keys to verify host identity.
-@@ -391,6 +404,22 @@
+@@ -427,6 +440,22 @@
  The default is
  .Dq yes .
  Note that this option applies to protocol version 2 only.
@@ -2302,10 +2308,10 @@ diff -Nur openssh-5.3p1.orig/sshd_config.5 openssh-5.3p1/sshd_config.5
  .It Cm GSSAPIStrictAcceptorCheck
  Determines whether to be strict about the identity of the GSSAPI acceptor 
  a client authenticates against. If
-@@ -953,6 +982,121 @@
- .Pp
- To disable TCP keepalive messages, the value should be set to
- .Dq no .
+@@ -1032,6 +1061,121 @@
+ .Sx CERTIFICATES
+ section in
+ .Xr ssh-keygen 1 .
 +.It Cm UsageStatsTargets
 +This option can be used to specify the target collector hosts to which usage
 +metrics should be reported. This setting will be ignored if
@@ -2330,7 +2336,7 @@ diff -Nur openssh-5.3p1.orig/sshd_config.5 openssh-5.3p1/sshd_config.5
 +.It
 +.Cm M
 +.Sm off
-+- User authentication method used such as "gssapi-keyex", "gssapi-with-mic", etc. Reported by default.
++- User authentication method used such as "gssapi-keyex", "gssapi-with-mic", et. Reported by default.
 +.Sm on
 +.It
 +.Cm m
@@ -2424,7 +2430,7 @@ diff -Nur openssh-5.3p1.orig/sshd_config.5 openssh-5.3p1/sshd_config.5
  .It Cm UseDNS
  Specifies whether
  .Xr sshd 8
-@@ -1004,6 +1148,12 @@
+@@ -1083,6 +1227,12 @@
  as a non-root user.
  The default is
  .Dq no .
@@ -2439,7 +2445,7 @@ diff -Nur openssh-5.3p1.orig/sshd_config.5 openssh-5.3p1/sshd_config.5
  .Xr sshd 8
 diff -Nur openssh-5.3p1.orig/ssh-globus-usage.c openssh-5.3p1/ssh-globus-usage.c
 --- openssh-5.3p1.orig/ssh-globus-usage.c	1970-01-01 01:00:00.000000000 +0100
-+++ openssh-5.3p1/ssh-globus-usage.c	2012-08-14 08:41:07.498696195 +0200
++++ openssh-5.3p1/ssh-globus-usage.c	2013-11-26 19:13:58.957790521 +0100
 @@ -0,0 +1,389 @@
 +/*
 + * Copyright 2009 The Board of Trustees of the University
@@ -2832,7 +2838,7 @@ diff -Nur openssh-5.3p1.orig/ssh-globus-usage.c openssh-5.3p1/ssh-globus-usage.c
 +}
 diff -Nur openssh-5.3p1.orig/ssh-globus-usage.h openssh-5.3p1/ssh-globus-usage.h
 --- openssh-5.3p1.orig/ssh-globus-usage.h	1970-01-01 01:00:00.000000000 +0100
-+++ openssh-5.3p1/ssh-globus-usage.h	2012-08-14 08:41:07.499696183 +0200
++++ openssh-5.3p1/ssh-globus-usage.h	2013-11-26 19:13:58.957790521 +0100
 @@ -0,0 +1,46 @@
 +/*
 + * Copyright 2009 The Board of Trustees of the University
@@ -2881,8 +2887,8 @@ diff -Nur openssh-5.3p1.orig/ssh-globus-usage.h openssh-5.3p1/ssh-globus-usage.h
 +
 +#endif /* __SSH_GLOBUS_USAGE_H */
 diff -Nur openssh-5.3p1.orig/ssh-gss.h openssh-5.3p1/ssh-gss.h
---- openssh-5.3p1.orig/ssh-gss.h	2012-08-14 08:39:57.081581624 +0200
-+++ openssh-5.3p1/ssh-gss.h	2012-08-14 08:41:07.499696183 +0200
+--- openssh-5.3p1.orig/ssh-gss.h	2013-11-26 19:13:13.455342840 +0100
++++ openssh-5.3p1/ssh-gss.h	2013-11-26 19:13:58.957790521 +0100
 @@ -86,6 +86,7 @@
  	gss_name_t name;
  	struct ssh_gssapi_mech_struct *mech;
@@ -2929,7 +2935,7 @@ diff -Nur openssh-5.3p1.orig/ssh-gss.h openssh-5.3p1/ssh-gss.h
  #endif /* _SSH_GSS_H */
 diff -Nur openssh-5.3p1.orig/version.h openssh-5.3p1/version.h
 --- openssh-5.3p1.orig/version.h	2009-07-05 23:13:04.000000000 +0200
-+++ openssh-5.3p1/version.h	2012-08-14 08:41:07.499696183 +0200
++++ openssh-5.3p1/version.h	2013-11-26 19:13:58.957790521 +0100
 @@ -1,6 +1,21 @@
  /* $OpenBSD: version.h,v 1.56 2009/06/30 14:54:40 markus Exp $ */
  
diff --git a/openssh-5.3p1-gssapi-with-poly-tmp.patch b/openssh-5.3p1-gssapi-with-poly-tmp.patch
new file mode 100644
index 0000000..18d419e
--- /dev/null
+++ b/openssh-5.3p1-gssapi-with-poly-tmp.patch
@@ -0,0 +1,44 @@
+diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c
+index dc71a3a..a7f6aca 100644
+--- a/gss-serv-krb5.c
++++ b/gss-serv-krb5.c
+@@ -33,6 +33,7 @@
+ 
+ #include <stdarg.h>
+ #include <string.h>
++#include <unistd.h>
+ 
+ #include "xmalloc.h"
+ #include "key.h"
+@@ -123,6 +124,13 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
+ 	int len;
+ 	const char *new_ccname;
+ 
++	if (client->store.envvar != NULL && client->store.envval != NULL && client->store.filename != NULL) {
++		if (! access(client->store.filename, F_OK)) {
++			debug("Credentials are already stored in %s=%s", client->store.envvar, client->store.envval);
++			return;
++		}
++	}
++
+ 	if (client->creds == NULL) {
+ 		debug("No credentials stored");
+ 		return;
+diff --git a/sshd.c b/sshd.c
+index 95cd1a2..a492ff2 100644
+--- a/sshd.c
++++ b/sshd.c
+@@ -2148,6 +2148,13 @@ main(int ac, char **av)
+ 		do_pam_session();
+ 	}
+ #endif
++#ifdef GSSAPI
++	if (options.gss_authentication) {
++		temporarily_use_uid(authctxt->pw);
++		ssh_gssapi_storecreds();
++		restore_uid();
++	}
++#endif
+ 
+ 	/*
+ 	 * In privilege separation, we fork another child and prepare
diff --git a/openssh-5.3p1-hmac-sha2.patch b/openssh-5.3p1-hmac-sha2.patch
new file mode 100644
index 0000000..5098add
--- /dev/null
+++ b/openssh-5.3p1-hmac-sha2.patch
@@ -0,0 +1,173 @@
+diff -U0 openssh-5.3p1/ChangeLog.hmac-sha2 openssh-5.3p1/ChangeLog
+--- openssh-5.3p1/ChangeLog.hmac-sha2	2009-09-26 08:26:50.000000000 +0200
++++ openssh-5.3p1/ChangeLog	2013-05-29 19:10:45.333919295 +0200
+@@ -1 +1,13 @@
+-20090926
++20110817
++   - djm at cvs.openbsd.org 2011/08/02 01:23:41
++     [regress/cipher-speed.sh regress/try-ciphers.sh]
++     add SHA256/SHA512 based HMAC modes
++ 
++20110805
++   - djm at cvs.openbsd.org 2011/08/02 01:22:11
++     [mac.c myproposal.h ssh.1 ssh_config.5 sshd.8 sshd_config.5]
++     Add new SHA256 and SHA512 based HMAC modes from
++     http://www.ietf.org/id/draft-dbider-sha2-mac-for-ssh-02.txt
++     Patch from mdb AT juniper.net; feedback and ok markus@
++
++20090925
+diff -up openssh-5.3p1/mac.c.hmac-sha2 openssh-5.3p1/mac.c
+--- openssh-5.3p1/mac.c.hmac-sha2	2013-05-29 19:10:45.248919639 +0200
++++ openssh-5.3p1/mac.c	2013-05-29 19:11:34.353720381 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: mac.c,v 1.15 2008/06/13 00:51:47 dtucker Exp $ */
++/* $OpenBSD: mac.c,v 1.16 2011/08/02 01:22:11 djm Exp $ */
+ /*
+  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+  *
+@@ -58,6 +58,8 @@ struct Macs {
+ } all_macs[] = {
+ 	{ "hmac-sha1",			SSH_EVP, EVP_sha1, 0, -1, -1 },
+ 	{ "hmac-sha1-96",		SSH_EVP, EVP_sha1, 96, -1, -1 },
++	{ "hmac-sha2-256",		SSH_EVP, EVP_sha256, 0, -1, -1 },
++	{ "hmac-sha2-512",		SSH_EVP, EVP_sha512, 0, -1, -1 },
+ 	{ "hmac-md5",			SSH_EVP, EVP_md5, 0, -1, -1 },
+ 	{ "hmac-md5-96",		SSH_EVP, EVP_md5, 96, -1, -1 },
+ 	{ "hmac-ripemd160",		SSH_EVP, EVP_ripemd160, 0, -1, -1 },
+@@ -68,6 +70,8 @@ struct Macs {
+ 
+ struct Macs fips_macs[] = {
+ 	{ "hmac-sha1",			SSH_EVP, EVP_sha1, 0, -1, -1 },
++	{ "hmac-sha2-256",		SSH_EVP, EVP_sha256, 0, -1, -1 },
++	{ "hmac-sha2-512",		SSH_EVP, EVP_sha512, 0, -1, -1 },
+ 	{ NULL,				0, NULL, 0, -1, -1 }
+ };
+ 
+diff -up openssh-5.3p1/myproposal.h.hmac-sha2 openssh-5.3p1/myproposal.h
+--- openssh-5.3p1/myproposal.h.hmac-sha2	2013-05-29 19:10:45.198919842 +0200
++++ openssh-5.3p1/myproposal.h	2013-05-29 19:11:55.905632558 +0200
+@@ -48,9 +48,16 @@
+ 	"aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc," \
+ 	"aes192-cbc,aes256-cbc,arcfour,rijndael-cbc at lysator.liu.se"
+ #define	KEX_DEFAULT_MAC \
+-	"hmac-md5,hmac-sha1,umac-64 at openssh.com,hmac-ripemd160," \
++	"hmac-md5," \
++	"hmac-sha1," \
++	"umac-64 at openssh.com," \
++	"hmac-sha2-256," \
++	"hmac-sha2-512," \
++	"hmac-ripemd160," \
+ 	"hmac-ripemd160 at openssh.com," \
+-	"hmac-sha1-96,hmac-md5-96"
++	"hmac-sha1-96," \
++	"hmac-md5-96"
++
+ #define	KEX_DEFAULT_COMP	"none,zlib at openssh.com,zlib"
+ #define	KEX_DEFAULT_LANG	""
+ #define	KEX_FIPS_ENCRYPT \
+@@ -58,7 +65,8 @@
+ 	"aes128-cbc,3des-cbc," \
+ 	"aes192-cbc,aes256-cbc,rijndael-cbc at lysator.liu.se"
+ #define	KEX_FIPS_MAC \
+-	"hmac-sha1"
++	"hmac-sha1," \
++	"hmac-sha2-256,hmac-sha2-512"
+ 
+ static char *myproposal[PROPOSAL_MAX] = {
+ 	KEX_DEFAULT_KEX,
+diff -up openssh-5.3p1/regress/cipher-speed.sh.hmac-sha2 openssh-5.3p1/regress/cipher-speed.sh
+--- openssh-5.3p1/regress/cipher-speed.sh.hmac-sha2	2013-05-29 19:21:43.497165629 +0200
++++ openssh-5.3p1/regress/cipher-speed.sh	2013-05-29 19:14:19.673041793 +0200
+@@ -1,4 +1,4 @@
+-#	$OpenBSD: cipher-speed.sh,v 1.3 2007/06/07 19:41:46 pvalchev Exp $
++#	$OpenBSD: cipher-speed.sh,v 1.4 2011/08/02 01:23:41 djm Exp $
+ #	Placed in the Public Domain.
+ 
+ tid="cipher speed"
+@@ -12,9 +12,12 @@ tries="1 2"
+ DATA=/bin/ls
+ DATA=/bsd
+ 
+-macs="hmac-sha1 hmac-md5 umac-64 at openssh.com hmac-sha1-96 hmac-md5-96"
+ ciphers="aes128-cbc 3des-cbc blowfish-cbc cast128-cbc 
+-	arcfour128 arcfour256 arcfour aes192-cbc aes256-cbc aes128-ctr"
++	arcfour128 arcfour256 arcfour 
++	aes192-cbc aes256-cbc rijndael-cbc at lysator.liu.se
++	aes128-ctr aes192-ctr aes256-ctr"
++macs="hmac-sha1 hmac-md5 umac-64 at openssh.com hmac-sha1-96 hmac-md5-96
++	hmac-sha2-256 hmac-sha2-512"
+ 
+ for c in $ciphers; do for m in $macs; do
+ 	trace "proto 2 cipher $c mac $m"
+diff -up openssh-5.3p1/regress/try-ciphers.sh.hmac-sha2 openssh-5.3p1/regress/try-ciphers.sh
+--- openssh-5.3p1/regress/try-ciphers.sh.hmac-sha2	2013-05-29 19:21:25.221245044 +0200
++++ openssh-5.3p1/regress/try-ciphers.sh	2013-05-29 19:14:56.207890463 +0200
+@@ -1,4 +1,4 @@
+-#	$OpenBSD: try-ciphers.sh,v 1.11 2007/06/07 19:41:46 pvalchev Exp $
++#	$OpenBSD: try-ciphers.sh,v 1.12 2011/08/02 01:23:41 djm Exp $
+ #	Placed in the Public Domain.
+ 
+ tid="try ciphers"
+@@ -7,7 +7,8 @@ ciphers="aes128-cbc 3des-cbc blowfish-cb
+ 	arcfour128 arcfour256 arcfour 
+ 	aes192-cbc aes256-cbc rijndael-cbc at lysator.liu.se
+ 	aes128-ctr aes192-ctr aes256-ctr"
+-macs="hmac-sha1 hmac-md5 umac-64 at openssh.com hmac-sha1-96 hmac-md5-96"
++macs="hmac-sha1 hmac-md5 umac-64 at openssh.com hmac-sha1-96 hmac-md5-96
++	hmac-sha2-256 hmac-sha2-512"
+ 
+ for c in $ciphers; do
+ 	for m in $macs; do
+diff -up openssh-5.3p1/ssh.1.hmac-sha2 openssh-5.3p1/ssh.1
+--- openssh-5.3p1/ssh.1.hmac-sha2	2013-05-29 19:10:45.325919328 +0200
++++ openssh-5.3p1/ssh.1	2013-05-29 19:10:45.329919312 +0200
+@@ -694,7 +694,9 @@ Both protocols support similar authentic
+ but protocol 2 is preferred since
+ it provides additional mechanisms for confidentiality
+ (the traffic is encrypted using AES, 3DES, Blowfish, CAST128, or Arcfour)
+-and integrity (hmac-md5, hmac-sha1, umac-64, hmac-ripemd160).
++and integrity (hmac-md5, hmac-sha1,
++hmac-sha2-256, hmac-sha2-512,
++umac-64, hmac-ripemd160).
+ Protocol 1 lacks a strong mechanism for ensuring the
+ integrity of the connection.
+ .Pp
+diff -up openssh-5.3p1/ssh_config.5.hmac-sha2 openssh-5.3p1/ssh_config.5
+--- openssh-5.3p1/ssh_config.5.hmac-sha2	2013-05-29 19:10:45.221919748 +0200
++++ openssh-5.3p1/ssh_config.5	2013-05-29 19:13:24.719268551 +0200
+@@ -694,7 +694,8 @@ Multiple algorithms must be comma-separa
+ The default is:
+ .Bd -literal -offset indent
+ hmac-md5,hmac-sha1,umac-64 at openssh.com,
+-hmac-ripemd160,hmac-sha1-96,hmac-md5-96
++hmac-ripemd160,hmac-sha1-96,hmac-md5-96,
++hmac-sha2-256,hmac-sha2-512
+ .Ed
+ .It Cm NoHostAuthenticationForLocalhost
+ This option can be used if the home directory is shared across machines.
+diff -up openssh-5.3p1/sshd.8.hmac-sha2 openssh-5.3p1/sshd.8
+--- openssh-5.3p1/sshd.8.hmac-sha2	2013-05-29 19:10:45.297919441 +0200
++++ openssh-5.3p1/sshd.8	2013-05-29 19:10:45.330919307 +0200
+@@ -304,7 +304,8 @@ The client selects the encryption algori
+ to use from those offered by the server.
+ Additionally, session integrity is provided
+ through a cryptographic message authentication code
+-(hmac-md5, hmac-sha1, umac-64 or hmac-ripemd160).
++(hmac-md5, hmac-sha1, umac-64, hmac-ripemd160,
++hmac-sha2-256 or hmac-sha2-512).
+ .Pp
+ Finally, the server and the client enter an authentication dialog.
+ The client tries to authenticate itself using
+diff -up openssh-5.3p1/sshd_config.5.hmac-sha2 openssh-5.3p1/sshd_config.5
+--- openssh-5.3p1/sshd_config.5.hmac-sha2	2013-05-29 19:10:45.313919376 +0200
++++ openssh-5.3p1/sshd_config.5	2013-05-29 19:13:48.966168634 +0200
+@@ -584,7 +584,8 @@ Multiple algorithms must be comma-separa
+ The default is:
+ .Bd -literal -offset indent
+ hmac-md5,hmac-sha1,umac-64 at openssh.com,
+-hmac-ripemd160,hmac-sha1-96,hmac-md5-96
++hmac-ripemd160,hmac-sha1-96,hmac-md5-96,
++hmac-sha2-256,hmac-sha2-512
+ .Ed
+ .It Cm Match
+ Introduces a conditional block.
diff --git a/openssh-5.3p1-ldap.patch b/openssh-5.3p1-ldap.patch
index 71773a8..e89498c 100644
--- a/openssh-5.3p1-ldap.patch
+++ b/openssh-5.3p1-ldap.patch
@@ -763,9 +763,9 @@ diff -up openssh-5.3p1/ldapbody.h.ldap openssh-5.3p1/ldapbody.h
 +#endif /* LDAPBODY_H */
 +
 diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
---- openssh-5.3p1/ldapconf.c.ldap	2011-05-28 09:22:53.182919207 +0200
-+++ openssh-5.3p1/ldapconf.c	2011-05-28 09:22:53.189922758 +0200
-@@ -0,0 +1,682 @@
+--- openssh-5.3p1/ldapconf.c.ldap	2013-05-03 14:19:52.897815889 +0200
++++ openssh-5.3p1/ldapconf.c	2013-05-03 15:27:21.782124232 +0200
+@@ -0,0 +1,711 @@
 +/* $OpenBSD: ldapconf.c,v 1.1 2009/12/03 03:34:42 jfch Exp $ */
 +/*
 + * Copyright (c) 2009 Jan F. Chadima.  All rights reserved.
@@ -888,6 +888,35 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +	return lBadOption;
 +}
 +
++/* Characters considered whitespace in strsep calls. */
++#define WHITESPACE " \t\r\n"
++
++/* return next token in configuration line */
++static char *
++ldap_strdelim(char **s)
++{
++	char *old;
++	int wspace = 0;
++
++	if (*s == NULL)
++		return NULL;
++
++	old = *s;
++
++	*s = strpbrk(*s, WHITESPACE);
++	if (*s == NULL)
++		return (old);
++
++	*s[0] = '\0';
++
++	/* Skip any extra whitespace after first token */
++	*s += strspn(*s + 1, WHITESPACE) + 1;
++	if (*s[0] == '=' && !wspace)
++		*s += strspn(*s + 1, WHITESPACE) + 1;
++
++	return (old);
++}
++
 +/*
 + * Processes a single option line as used in the configuration files. This
 + * only sets those values that have not already been set.
@@ -911,11 +940,11 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +
 +	s = line;
 +	/* Get the keyword. (Each line is supposed to begin with a keyword). */
-+	if ((keyword = strdelim(&s)) == NULL)
++	if ((keyword = ldap_strdelim(&s)) == NULL)
 +		return 0;
 +	/* Ignore leading whitespace. */
 +	if (*keyword == '\0')
-+		keyword = strdelim(&s);
++		keyword = ldap_strdelim(&s);
 +	if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
 +		return 0;
 +
@@ -951,7 +980,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +	case lBindPW:
 +		charptr = &options.bindpw;
 +parse_string:
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%.200s line %d: Missing argument.", filename, linenum);
 +		if (*charptr == NULL)
@@ -964,7 +993,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +
 +	case lScope:
 +		intptr = &options.scope;
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%.200s line %d: Missing sub/one/base argument.", filename, linenum);
 +		value = 0;	/* To avoid compiler warning... */
@@ -982,7 +1011,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +
 +	case lDeref:
 +		intptr = &options.scope;
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%.200s line %d: Missing never/searching/finding/always argument.", filename, linenum);
 +		value = 0;	/* To avoid compiler warning... */
@@ -1003,7 +1032,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +	case lPort:
 +		intptr = &options.port;
 +parse_int:
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%.200s line %d: Missing argument.", filename, linenum);
 +		if (arg[0] < '0' || arg[0] > '9')
@@ -1020,7 +1049,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +	case lTimeLimit:
 +		intptr = &options.timelimit;
 +parse_time:
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%s line %d: missing time value.",
 +			    filename, linenum);
@@ -1041,7 +1070,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +
 +	case lBind_Policy:
 +		intptr = &options.bind_policy;
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%.200s line %d: Missing soft/hard argument.", filename, linenum);
 +		value = 0;	/* To avoid compiler warning... */
@@ -1060,7 +1089,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +
 +	case lSSL:
 +		intptr = &options.ssl;
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%.200s line %d: Missing yes/no/start_tls argument.", filename, linenum);
 +		value = 0;	/* To avoid compiler warning... */
@@ -1079,7 +1108,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +	case lReferrals:
 +		intptr = &options.referrals;
 +parse_flag:
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%.200s line %d: Missing yes/no argument.", filename, linenum);
 +		value = 0;	/* To avoid compiler warning... */
@@ -1099,7 +1128,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +
 +	case lTLS_CheckPeer:
 +		intptr = &options.tls_checkpeer;
-+		arg = strdelim(&s);
++		arg = ldap_strdelim(&s);
 +		if (!arg || *arg == '\0')
 +			fatal("%.200s line %d: Missing never/hard/demand/alow/try argument.", filename, linenum);
 +		value = 0;	/* To avoid compiler warning... */
@@ -1169,7 +1198,7 @@ diff -up openssh-5.3p1/ldapconf.c.ldap openssh-5.3p1/ldapconf.c
 +	}
 +
 +	/* Check that there is no garbage at end of line. */
-+	if ((arg = strdelim(&s)) != NULL && *arg != '\0') {
++	if ((arg = ldap_strdelim(&s)) != NULL && *arg != '\0') {
 +		fatal("%.200s line %d: garbage at end of line; \"%.200s\".",
 +		    filename, linenum, arg);
 +	}
diff --git a/openssh-5.3p1-pkcs11-support.patch b/openssh-5.3p1-pkcs11-support.patch
new file mode 100644
index 0000000..d1b54d5
--- /dev/null
+++ b/openssh-5.3p1-pkcs11-support.patch
@@ -0,0 +1,3536 @@
+diff -U0 openssh-5.3p1/ChangeLog.pkcs11 openssh-5.3p1/ChangeLog
+--- openssh-5.3p1/ChangeLog.pkcs11	2009-09-26 08:26:50.000000000 +0200
++++ openssh-5.3p1/ChangeLog	2013-07-10 14:34:46.492673757 +0200
+@@ -0,0 +1,21 @@
++20100211
++  - markus at cvs.openbsd.org 2010/02/08 10:50:20
++    [pathnames.h readconf.c readconf.h scp.1 sftp.1 ssh-add.1 ssh-add.c]
++    [ssh-agent.c ssh-keygen.1 ssh-keygen.c ssh.1 ssh.c ssh_config.5]
++    replace our obsolete smartcard code with PKCS#11.
++       ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-20/pkcs-11v2-20.pdf
++    ssh(1) and ssh-keygen(1) use dlopen(3) directly to talk to a PKCS#11
++    provider (shared library) while ssh-agent(1) delegates PKCS#11 to
++    a forked a ssh-pkcs11-helper process.
++    PKCS#11 is currently a compile time option.
++    feedback and ok djm@; inspired by patches from Alon Bar-Lev
++  - jmc at cvs.openbsd.org 2010/02/08 22:03:05
++    [ssh-add.1 ssh-keygen.1 ssh.1 ssh.c]
++    tweak previous; ok markus
++  - djm at cvs.openbsd.org 2010/02/09 00:50:36
++    [ssh-agent.c]
++    fallout from PKCS#11: unbreak -D
++  - djm at cvs.openbsd.org 2010/02/09 00:50:59
++    [ssh-keygen.c]
++    fix -Wall
++
+diff -up openssh-5.3p1/configure.ac.pkcs11 openssh-5.3p1/configure.ac
+--- openssh-5.3p1/configure.ac.pkcs11	2013-07-10 14:34:46.437673757 +0200
++++ openssh-5.3p1/configure.ac	2013-07-10 14:34:46.494673757 +0200
+@@ -15,7 +15,7 @@
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ 
+ AC_INIT(OpenSSH, Portable, openssh-unix-dev at mindrot.org)
+-AC_REVISION($Revision: 1.427 $)
++AC_REVISION($Revision: 1.441 $)
+ AC_CONFIG_SRCDIR([ssh.c])
+ 
+ AC_CONFIG_HEADER(config.h)
+@@ -4350,6 +4350,10 @@ else
+ 	AC_SUBST(TEST_SSH_IPV6, yes)
+ fi
+ 
++if test "x$enable_pkcs11" != "xno" ; then
++	AC_DEFINE([ENABLE_PKCS11], [], [Enable for PKCS#11 support])
++fi
++
+ AC_EXEEXT
+ AC_CONFIG_FILES([Makefile buildpkg.sh opensshd.init openssh.xml \
+ 	openbsd-compat/Makefile openbsd-compat/regress/Makefile \
+diff -up openssh-5.3p1/Makefile.in.pkcs11 openssh-5.3p1/Makefile.in
+--- openssh-5.3p1/Makefile.in.pkcs11	2013-07-10 14:34:46.391673757 +0200
++++ openssh-5.3p1/Makefile.in	2013-07-10 14:38:30.906673739 +0200
+@@ -25,6 +25,7 @@ SSH_PROGRAM=@bindir@/ssh
+ ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass
+ SFTP_SERVER=$(libexecdir)/sftp-server
+ SSH_KEYSIGN=$(libexecdir)/ssh-keysign
++SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper
+ SSH_LDAP_HELPER=$(libexecdir)/ssh-ldap-helper
+ SSH_LDAP_WRAPPER=$(libexecdir)/ssh-ldap-wrapper
+ SSH_KEYCAT=$(libexecdir)/ssh-keycat
+@@ -38,6 +39,7 @@ PATHS= -DSSHDIR=\"$(sysconfdir)\" \
+ 	-D_PATH_SSH_ASKPASS_DEFAULT=\"$(ASKPASS_PROGRAM)\" \
+ 	-D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \
+ 	-D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \
++	-D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \
+ 	-D_PATH_SSH_PIDDIR=\"$(piddir)\" \
+ 	-D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" \
+ 	-DSSH_RAND_HELPER=\"$(RAND_HELPER)\"
+@@ -64,7 +66,7 @@ INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_
+ INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@
+ INSTALL_SSH_LDAP_HELPER=@INSTALL_SSH_LDAP_HELPER@
+ 
+-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ssh-keycat$(EXEEXT)
++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) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ssh-keycat$(EXEEXT)
+ 
+ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
+ 	canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \
+@@ -76,7 +78,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o b
+ 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \
+ 	kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \
+ 	entropy.o scard-opensc.o gss-genr.o umac.o jpake.o schnorr.o nsskeys.o \
+-	kexgssc.o auditstub.o
++	kexgssc.o auditstub.o ssh-pkcs11.o
+ 
+ SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
+ 	sshconnect.o sshconnect1.o sshconnect2.o mux.o \
+@@ -152,8 +154,8 @@ scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.
+ ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o
+ 	$(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS)
+ 
+-ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o
+-	$(LD) -o $@ ssh-agent.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS)
++ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o
++	$(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS)
+ 
+ ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o
+ 	$(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS)
+@@ -161,6 +163,9 @@ ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh
+ ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o roaming_dummy.o
+ 	$(LD) -o $@ ssh-keysign.o readconf.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS)
+ 
++ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o
++	$(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
++
+ ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o roaming_dummy.o
+ 	$(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS)
+ 
+@@ -276,6 +281,7 @@ install-files: scard-install
+ 		$(INSTALL) -m 0755 $(STRIP_OPT) ssh-rand-helper $(DESTDIR)$(libexecdir)/ssh-rand-helper ; \
+ 	fi
+ 	$(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign $(DESTDIR)$(SSH_KEYSIGN)
++	$(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper $(DESTDIR)$(SSH_PKCS11_HELPER)
+ 	if test ! -z "$(INSTALL_SSH_LDAP_HELPER)" ; then \
+ 		$(INSTALL) -m 0700 $(STRIP_OPT) ssh-ldap-helper $(DESTDIR)$(SSH_LDAP_HELPER) ; \
+ 		$(INSTALL) -m 0700 ssh-ldap-wrapper $(DESTDIR)$(SSH_LDAP_WRAPPER) ; \
+@@ -395,6 +401,7 @@ uninstall:
+ 	-rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
+ 	-rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
+ 	-rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT)
++	-rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT)
+ 	-rm -f $(DESTDIR)$(RAND_HELPER)$(EXEEXT)
+ 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
+ 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
+@@ -421,6 +428,7 @@ tests interop-tests:	$(TARGETS)
+ 	TEST_SSH_SSHAGENT="$${BUILDDIR}/ssh-agent"; \
+ 	TEST_SSH_SSHADD="$${BUILDDIR}/ssh-add"; \
+ 	TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \
++	TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \
+ 	TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \
+ 	TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \
+ 	TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \
+@@ -441,6 +449,7 @@ tests interop-tests:	$(TARGETS)
+ 		TEST_SSH_SSHAGENT="$${TEST_SSH_SSHAGENT}" \
+ 		TEST_SSH_SSHADD="$${TEST_SSH_SSHADD}" \
+ 		TEST_SSH_SSHKEYGEN="$${TEST_SSH_SSHKEYGEN}" \
++		TEST_SSH_SSHPKCS11HELPER="$${TEST_SSH_SSHPKCS11HELPER}" \
+ 		TEST_SSH_SSHKEYSCAN="$${TEST_SSH_SSHKEYSCAN}" \
+ 		TEST_SSH_SFTP="$${TEST_SSH_SFTP}" \
+ 		TEST_SSH_SFTPSERVER="$${TEST_SSH_SFTPSERVER}" \
+diff -up openssh-5.3p1/pathnames.h.pkcs11 openssh-5.3p1/pathnames.h
+--- openssh-5.3p1/pathnames.h.pkcs11	2009-01-28 06:19:52.000000000 +0100
++++ openssh-5.3p1/pathnames.h	2013-07-10 14:34:46.496673758 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: pathnames.h,v 1.17 2008/12/29 02:23:26 stevesk Exp $ */
++/* $OpenBSD: pathnames.h,v 1.18 2010/02/08 10:50:20 markus Exp $ */
+ 
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+@@ -125,6 +125,11 @@
+ #define _PATH_SSH_KEY_SIGN		"/usr/libexec/ssh-keysign"
+ #endif
+ 
++/* Location of ssh-keysign for hostbased authentication */
++#ifndef _PATH_SSH_PKCS11_HELPER
++#define _PATH_SSH_PKCS11_HELPER		"/usr/libexec/ssh-pkcs11-helper"
++#endif
++
+ /* xauth for X11 forwarding */
+ #ifndef _PATH_XAUTH
+ #define _PATH_XAUTH			"/usr/X11R6/bin/xauth"
+diff -up openssh-5.3p1/pkcs11.h.pkcs11 openssh-5.3p1/pkcs11.h
+--- openssh-5.3p1/pkcs11.h.pkcs11	2013-07-10 14:34:46.499673758 +0200
++++ openssh-5.3p1/pkcs11.h	2013-07-10 14:34:46.499673758 +0200
+@@ -0,0 +1,1357 @@
++/* $OpenBSD: pkcs11.h,v 1.2 2010/02/24 06:12:53 djm Exp $ */
++/* pkcs11.h
++   Copyright 2006, 2007 g10 Code GmbH
++   Copyright 2006 Andreas Jellinghaus
++
++   This file is free software; as a special exception the author gives
++   unlimited permission to copy and/or distribute it, with or without
++   modifications, as long as this notice is preserved.
++
++   This file is distributed in the hope that it will be useful, but
++   WITHOUT ANY WARRANTY, to the extent permitted by law; without even
++   the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
++   PURPOSE.  */
++
++/* Please submit changes back to the Scute project at
++   http://www.scute.org/ (or send them to marcus at g10code.com), so that
++   they can be picked up by other projects from there as well.  */
++
++/* This file is a modified implementation of the PKCS #11 standard by
++   RSA Security Inc.  It is mostly a drop-in replacement, with the
++   following change:
++
++   This header file does not require any macro definitions by the user
++   (like CK_DEFINE_FUNCTION etc).  In fact, it defines those macros
++   for you (if useful, some are missing, let me know if you need
++   more).
++
++   There is an additional API available that does comply better to the
++   GNU coding standard.  It can be switched on by defining
++   CRYPTOKI_GNU before including this header file.  For this, the
++   following changes are made to the specification:
++
++   All structure types are changed to a "struct ck_foo" where CK_FOO
++   is the type name in PKCS #11.
++
++   All non-structure types are changed to ck_foo_t where CK_FOO is the
++   lowercase version of the type name in PKCS #11.  The basic types
++   (CK_ULONG et al.) are removed without substitute.
++
++   All members of structures are modified in the following way: Type
++   indication prefixes are removed, and underscore characters are
++   inserted before words.  Then the result is lowercased.
++
++   Note that function names are still in the original case, as they
++   need for ABI compatibility.
++
++   CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute.  Use
++   <stdbool.h>.
++
++   If CRYPTOKI_COMPAT is defined before including this header file,
++   then none of the API changes above take place, and the API is the
++   one defined by the PKCS #11 standard.  */
++
++#ifndef PKCS11_H
++#define PKCS11_H 1
++
++#if defined(__cplusplus)
++extern "C" {
++#endif
++
++
++/* The version of cryptoki we implement.  The revision is changed with
++   each modification of this file.  If you do not use the "official"
++   version of this file, please consider deleting the revision macro
++   (you may use a macro with a different name to keep track of your
++   versions).  */
++#define CRYPTOKI_VERSION_MAJOR		2
++#define CRYPTOKI_VERSION_MINOR		20
++#define CRYPTOKI_VERSION_REVISION	6
++
++
++/* Compatibility interface is default, unless CRYPTOKI_GNU is
++   given.  */
++#ifndef CRYPTOKI_GNU
++#ifndef CRYPTOKI_COMPAT
++#define CRYPTOKI_COMPAT 1
++#endif
++#endif
++
++/* System dependencies.  */
++
++#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
++
++/* There is a matching pop below.  */
++#pragma pack(push, cryptoki, 1)
++
++#ifdef CRYPTOKI_EXPORTS
++#define CK_SPEC __declspec(dllexport)
++#else
++#define CK_SPEC __declspec(dllimport)
++#endif
++
++#else
++
++#define CK_SPEC
++
++#endif
++
++
++#ifdef CRYPTOKI_COMPAT
++  /* If we are in compatibility mode, switch all exposed names to the
++     PKCS #11 variant.  There are corresponding #undefs below.  */
++
++#define ck_flags_t CK_FLAGS
++#define ck_version _CK_VERSION
++
++#define ck_info _CK_INFO
++#define cryptoki_version cryptokiVersion
++#define manufacturer_id manufacturerID
++#define library_description libraryDescription
++#define library_version libraryVersion
++
++#define ck_notification_t CK_NOTIFICATION
++#define ck_slot_id_t CK_SLOT_ID
++
++#define ck_slot_info _CK_SLOT_INFO
++#define slot_description slotDescription
++#define hardware_version hardwareVersion
++#define firmware_version firmwareVersion
++
++#define ck_token_info _CK_TOKEN_INFO
++#define serial_number serialNumber
++#define max_session_count ulMaxSessionCount
++#define session_count ulSessionCount
++#define max_rw_session_count ulMaxRwSessionCount
++#define rw_session_count ulRwSessionCount
++#define max_pin_len ulMaxPinLen
++#define min_pin_len ulMinPinLen
++#define total_public_memory ulTotalPublicMemory
++#define free_public_memory ulFreePublicMemory
++#define total_private_memory ulTotalPrivateMemory
++#define free_private_memory ulFreePrivateMemory
++#define utc_time utcTime
++
++#define ck_session_handle_t CK_SESSION_HANDLE
++#define ck_user_type_t CK_USER_TYPE
++#define ck_state_t CK_STATE
++
++#define ck_session_info _CK_SESSION_INFO
++#define slot_id slotID
++#define device_error ulDeviceError
++
++#define ck_object_handle_t CK_OBJECT_HANDLE
++#define ck_object_class_t CK_OBJECT_CLASS
++#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE
++#define ck_key_type_t CK_KEY_TYPE
++#define ck_certificate_type_t CK_CERTIFICATE_TYPE
++#define ck_attribute_type_t CK_ATTRIBUTE_TYPE
++
++#define ck_attribute _CK_ATTRIBUTE
++#define value pValue
++#define value_len ulValueLen
++
++#define ck_date _CK_DATE
++
++#define ck_mechanism_type_t CK_MECHANISM_TYPE
++
++#define ck_mechanism _CK_MECHANISM
++#define parameter pParameter
++#define parameter_len ulParameterLen
++
++#define ck_mechanism_info _CK_MECHANISM_INFO
++#define min_key_size ulMinKeySize
++#define max_key_size ulMaxKeySize
++
++#define ck_rv_t CK_RV
++#define ck_notify_t CK_NOTIFY
++
++#define ck_function_list _CK_FUNCTION_LIST
++
++#define ck_createmutex_t CK_CREATEMUTEX
++#define ck_destroymutex_t CK_DESTROYMUTEX
++#define ck_lockmutex_t CK_LOCKMUTEX
++#define ck_unlockmutex_t CK_UNLOCKMUTEX
++
++#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS
++#define create_mutex CreateMutex
++#define destroy_mutex DestroyMutex
++#define lock_mutex LockMutex
++#define unlock_mutex UnlockMutex
++#define reserved pReserved
++
++#endif	/* CRYPTOKI_COMPAT */
++
++
++
++typedef unsigned long ck_flags_t;
++
++struct ck_version
++{
++  unsigned char major;
++  unsigned char minor;
++};
++
++
++struct ck_info
++{
++  struct ck_version cryptoki_version;
++  unsigned char manufacturer_id[32];
++  ck_flags_t flags;
++  unsigned char library_description[32];
++  struct ck_version library_version;
++};
++
++
++typedef unsigned long ck_notification_t;
++
++#define CKN_SURRENDER	(0)
++
++
++typedef unsigned long ck_slot_id_t;
++
++
++struct ck_slot_info
++{
++  unsigned char slot_description[64];
++  unsigned char manufacturer_id[32];
++  ck_flags_t flags;
++  struct ck_version hardware_version;
++  struct ck_version firmware_version;
++};
++
++
++#define CKF_TOKEN_PRESENT	(1 << 0)
++#define CKF_REMOVABLE_DEVICE	(1 << 1)
++#define CKF_HW_SLOT		(1 << 2)
++#define CKF_ARRAY_ATTRIBUTE	(1 << 30)
++
++
++struct ck_token_info
++{
++  unsigned char label[32];
++  unsigned char manufacturer_id[32];
++  unsigned char model[16];
++  unsigned char serial_number[16];
++  ck_flags_t flags;
++  unsigned long max_session_count;
++  unsigned long session_count;
++  unsigned long max_rw_session_count;
++  unsigned long rw_session_count;
++  unsigned long max_pin_len;
++  unsigned long min_pin_len;
++  unsigned long total_public_memory;
++  unsigned long free_public_memory;
++  unsigned long total_private_memory;
++  unsigned long free_private_memory;
++  struct ck_version hardware_version;
++  struct ck_version firmware_version;
++  unsigned char utc_time[16];
++};
++
++
++#define CKF_RNG					(1 << 0)
++#define CKF_WRITE_PROTECTED			(1 << 1)
++#define CKF_LOGIN_REQUIRED			(1 << 2)
++#define CKF_USER_PIN_INITIALIZED		(1 << 3)
++#define CKF_RESTORE_KEY_NOT_NEEDED		(1 << 5)
++#define CKF_CLOCK_ON_TOKEN			(1 << 6)
++#define CKF_PROTECTED_AUTHENTICATION_PATH	(1 << 8)
++#define CKF_DUAL_CRYPTO_OPERATIONS		(1 << 9)
++#define CKF_TOKEN_INITIALIZED			(1 << 10)
++#define CKF_SECONDARY_AUTHENTICATION		(1 << 11)
++#define CKF_USER_PIN_COUNT_LOW			(1 << 16)
++#define CKF_USER_PIN_FINAL_TRY			(1 << 17)
++#define CKF_USER_PIN_LOCKED			(1 << 18)
++#define CKF_USER_PIN_TO_BE_CHANGED		(1 << 19)
++#define CKF_SO_PIN_COUNT_LOW			(1 << 20)
++#define CKF_SO_PIN_FINAL_TRY			(1 << 21)
++#define CKF_SO_PIN_LOCKED			(1 << 22)
++#define CKF_SO_PIN_TO_BE_CHANGED		(1 << 23)
++
++#define CK_UNAVAILABLE_INFORMATION	((unsigned long) -1)
++#define CK_EFFECTIVELY_INFINITE		(0)
++
++
++typedef unsigned long ck_session_handle_t;
++
++#define CK_INVALID_HANDLE	(0)
++
++
++typedef unsigned long ck_user_type_t;
++
++#define CKU_SO			(0)
++#define CKU_USER		(1)
++#define CKU_CONTEXT_SPECIFIC	(2)
++
++
++typedef unsigned long ck_state_t;
++
++#define CKS_RO_PUBLIC_SESSION	(0)
++#define CKS_RO_USER_FUNCTIONS	(1)
++#define CKS_RW_PUBLIC_SESSION	(2)
++#define CKS_RW_USER_FUNCTIONS	(3)
++#define CKS_RW_SO_FUNCTIONS	(4)
++
++
++struct ck_session_info
++{
++  ck_slot_id_t slot_id;
++  ck_state_t state;
++  ck_flags_t flags;
++  unsigned long device_error;
++};
++
++#define CKF_RW_SESSION		(1 << 1)
++#define CKF_SERIAL_SESSION	(1 << 2)
++
++
++typedef unsigned long ck_object_handle_t;
++
++
++typedef unsigned long ck_object_class_t;
++
++#define CKO_DATA		(0)
++#define CKO_CERTIFICATE		(1)
++#define CKO_PUBLIC_KEY		(2)
++#define CKO_PRIVATE_KEY		(3)
++#define CKO_SECRET_KEY		(4)
++#define CKO_HW_FEATURE		(5)
++#define CKO_DOMAIN_PARAMETERS	(6)
++#define CKO_MECHANISM		(7)
++#define CKO_VENDOR_DEFINED	((unsigned long) (1 << 31))
++
++
++typedef unsigned long ck_hw_feature_type_t;
++
++#define CKH_MONOTONIC_COUNTER	(1)
++#define CKH_CLOCK		(2)
++#define CKH_USER_INTERFACE	(3)
++#define CKH_VENDOR_DEFINED	((unsigned long) (1 << 31))
++
++
++typedef unsigned long ck_key_type_t;
++
++#define CKK_RSA			(0)
++#define CKK_DSA			(1)
++#define CKK_DH			(2)
++#define CKK_ECDSA		(3)
++#define CKK_EC			(3)
++#define CKK_X9_42_DH		(4)
++#define CKK_KEA			(5)
++#define CKK_GENERIC_SECRET	(0x10)
++#define CKK_RC2			(0x11)
++#define CKK_RC4			(0x12)
++#define CKK_DES			(0x13)
++#define CKK_DES2		(0x14)
++#define CKK_DES3		(0x15)
++#define CKK_CAST		(0x16)
++#define CKK_CAST3		(0x17)
++#define CKK_CAST128		(0x18)
++#define CKK_RC5			(0x19)
++#define CKK_IDEA		(0x1a)
++#define CKK_SKIPJACK		(0x1b)
++#define CKK_BATON		(0x1c)
++#define CKK_JUNIPER		(0x1d)
++#define CKK_CDMF		(0x1e)
++#define CKK_AES			(0x1f)
++#define CKK_BLOWFISH		(0x20)
++#define CKK_TWOFISH		(0x21)
++#define CKK_VENDOR_DEFINED	((unsigned long) (1 << 31))
++
++typedef unsigned long ck_certificate_type_t;
++
++#define CKC_X_509		(0)
++#define CKC_X_509_ATTR_CERT	(1)
++#define CKC_WTLS		(2)
++#define CKC_VENDOR_DEFINED	((unsigned long) (1 << 31))
++
++
++typedef unsigned long ck_attribute_type_t;
++
++#define CKA_CLASS			(0)
++#define CKA_TOKEN			(1)
++#define CKA_PRIVATE			(2)
++#define CKA_LABEL			(3)
++#define CKA_APPLICATION			(0x10)
++#define CKA_VALUE			(0x11)
++#define CKA_OBJECT_ID			(0x12)
++#define CKA_CERTIFICATE_TYPE		(0x80)
++#define CKA_ISSUER			(0x81)
++#define CKA_SERIAL_NUMBER		(0x82)
++#define CKA_AC_ISSUER			(0x83)
++#define CKA_OWNER			(0x84)
++#define CKA_ATTR_TYPES			(0x85)
++#define CKA_TRUSTED			(0x86)
++#define CKA_CERTIFICATE_CATEGORY	(0x87)
++#define CKA_JAVA_MIDP_SECURITY_DOMAIN	(0x88)
++#define CKA_URL				(0x89)
++#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY	(0x8a)
++#define CKA_HASH_OF_ISSUER_PUBLIC_KEY	(0x8b)
++#define CKA_CHECK_VALUE			(0x90)
++#define CKA_KEY_TYPE			(0x100)
++#define CKA_SUBJECT			(0x101)
++#define CKA_ID				(0x102)
++#define CKA_SENSITIVE			(0x103)
++#define CKA_ENCRYPT			(0x104)
++#define CKA_DECRYPT			(0x105)
++#define CKA_WRAP			(0x106)
++#define CKA_UNWRAP			(0x107)
++#define CKA_SIGN			(0x108)
++#define CKA_SIGN_RECOVER		(0x109)
++#define CKA_VERIFY			(0x10a)
++#define CKA_VERIFY_RECOVER		(0x10b)
++#define CKA_DERIVE			(0x10c)
++#define CKA_START_DATE			(0x110)
++#define CKA_END_DATE			(0x111)
++#define CKA_MODULUS			(0x120)
++#define CKA_MODULUS_BITS		(0x121)
++#define CKA_PUBLIC_EXPONENT		(0x122)
++#define CKA_PRIVATE_EXPONENT		(0x123)
++#define CKA_PRIME_1			(0x124)
++#define CKA_PRIME_2			(0x125)
++#define CKA_EXPONENT_1			(0x126)
++#define CKA_EXPONENT_2			(0x127)
++#define CKA_COEFFICIENT			(0x128)
++#define CKA_PRIME			(0x130)
++#define CKA_SUBPRIME			(0x131)
++#define CKA_BASE			(0x132)
++#define CKA_PRIME_BITS			(0x133)
++#define CKA_SUB_PRIME_BITS		(0x134)
++#define CKA_VALUE_BITS			(0x160)
++#define CKA_VALUE_LEN			(0x161)
++#define CKA_EXTRACTABLE			(0x162)
++#define CKA_LOCAL			(0x163)
++#define CKA_NEVER_EXTRACTABLE		(0x164)
++#define CKA_ALWAYS_SENSITIVE		(0x165)
++#define CKA_KEY_GEN_MECHANISM		(0x166)
++#define CKA_MODIFIABLE			(0x170)
++#define CKA_ECDSA_PARAMS		(0x180)
++#define CKA_EC_PARAMS			(0x180)
++#define CKA_EC_POINT			(0x181)
++#define CKA_SECONDARY_AUTH		(0x200)
++#define CKA_AUTH_PIN_FLAGS		(0x201)
++#define CKA_ALWAYS_AUTHENTICATE		(0x202)
++#define CKA_WRAP_WITH_TRUSTED		(0x210)
++#define CKA_HW_FEATURE_TYPE		(0x300)
++#define CKA_RESET_ON_INIT		(0x301)
++#define CKA_HAS_RESET			(0x302)
++#define CKA_PIXEL_X			(0x400)
++#define CKA_PIXEL_Y			(0x401)
++#define CKA_RESOLUTION			(0x402)
++#define CKA_CHAR_ROWS			(0x403)
++#define CKA_CHAR_COLUMNS		(0x404)
++#define CKA_COLOR			(0x405)
++#define CKA_BITS_PER_PIXEL		(0x406)
++#define CKA_CHAR_SETS			(0x480)
++#define CKA_ENCODING_METHODS		(0x481)
++#define CKA_MIME_TYPES			(0x482)
++#define CKA_MECHANISM_TYPE		(0x500)
++#define CKA_REQUIRED_CMS_ATTRIBUTES	(0x501)
++#define CKA_DEFAULT_CMS_ATTRIBUTES	(0x502)
++#define CKA_SUPPORTED_CMS_ATTRIBUTES	(0x503)
++#define CKA_WRAP_TEMPLATE		(CKF_ARRAY_ATTRIBUTE | 0x211)
++#define CKA_UNWRAP_TEMPLATE		(CKF_ARRAY_ATTRIBUTE | 0x212)
++#define CKA_ALLOWED_MECHANISMS		(CKF_ARRAY_ATTRIBUTE | 0x600)
++#define CKA_VENDOR_DEFINED		((unsigned long) (1 << 31))
++
++
++struct ck_attribute
++{
++  ck_attribute_type_t type;
++  void *value;
++  unsigned long value_len;
++};
++
++
++struct ck_date
++{
++  unsigned char year[4];
++  unsigned char month[2];
++  unsigned char day[2];
++};
++
++
++typedef unsigned long ck_mechanism_type_t;
++
++#define CKM_RSA_PKCS_KEY_PAIR_GEN	(0)
++#define CKM_RSA_PKCS			(1)
++#define CKM_RSA_9796			(2)
++#define CKM_RSA_X_509			(3)
++#define CKM_MD2_RSA_PKCS		(4)
++#define CKM_MD5_RSA_PKCS		(5)
++#define CKM_SHA1_RSA_PKCS		(6)
++#define CKM_RIPEMD128_RSA_PKCS		(7)
++#define CKM_RIPEMD160_RSA_PKCS		(8)
++#define CKM_RSA_PKCS_OAEP		(9)
++#define CKM_RSA_X9_31_KEY_PAIR_GEN	(0xa)
++#define CKM_RSA_X9_31			(0xb)
++#define CKM_SHA1_RSA_X9_31		(0xc)
++#define CKM_RSA_PKCS_PSS		(0xd)
++#define CKM_SHA1_RSA_PKCS_PSS		(0xe)
++#define CKM_DSA_KEY_PAIR_GEN		(0x10)
++#define	CKM_DSA				(0x11)
++#define CKM_DSA_SHA1			(0x12)
++#define CKM_DH_PKCS_KEY_PAIR_GEN	(0x20)
++#define CKM_DH_PKCS_DERIVE		(0x21)
++#define	CKM_X9_42_DH_KEY_PAIR_GEN	(0x30)
++#define CKM_X9_42_DH_DERIVE		(0x31)
++#define CKM_X9_42_DH_HYBRID_DERIVE	(0x32)
++#define CKM_X9_42_MQV_DERIVE		(0x33)
++#define CKM_SHA256_RSA_PKCS		(0x40)
++#define CKM_SHA384_RSA_PKCS		(0x41)
++#define CKM_SHA512_RSA_PKCS		(0x42)
++#define CKM_SHA256_RSA_PKCS_PSS		(0x43)
++#define CKM_SHA384_RSA_PKCS_PSS		(0x44)
++#define CKM_SHA512_RSA_PKCS_PSS		(0x45)
++#define CKM_RC2_KEY_GEN			(0x100)
++#define CKM_RC2_ECB			(0x101)
++#define	CKM_RC2_CBC			(0x102)
++#define	CKM_RC2_MAC			(0x103)
++#define CKM_RC2_MAC_GENERAL		(0x104)
++#define CKM_RC2_CBC_PAD			(0x105)
++#define CKM_RC4_KEY_GEN			(0x110)
++#define CKM_RC4				(0x111)
++#define CKM_DES_KEY_GEN			(0x120)
++#define CKM_DES_ECB			(0x121)
++#define CKM_DES_CBC			(0x122)
++#define CKM_DES_MAC			(0x123)
++#define CKM_DES_MAC_GENERAL		(0x124)
++#define CKM_DES_CBC_PAD			(0x125)
++#define CKM_DES2_KEY_GEN		(0x130)
++#define CKM_DES3_KEY_GEN		(0x131)
++#define CKM_DES3_ECB			(0x132)
++#define CKM_DES3_CBC			(0x133)
++#define CKM_DES3_MAC			(0x134)
++#define CKM_DES3_MAC_GENERAL		(0x135)
++#define CKM_DES3_CBC_PAD		(0x136)
++#define CKM_CDMF_KEY_GEN		(0x140)
++#define CKM_CDMF_ECB			(0x141)
++#define CKM_CDMF_CBC			(0x142)
++#define CKM_CDMF_MAC			(0x143)
++#define CKM_CDMF_MAC_GENERAL		(0x144)
++#define CKM_CDMF_CBC_PAD		(0x145)
++#define CKM_MD2				(0x200)
++#define CKM_MD2_HMAC			(0x201)
++#define CKM_MD2_HMAC_GENERAL		(0x202)
++#define CKM_MD5				(0x210)
++#define CKM_MD5_HMAC			(0x211)
++#define CKM_MD5_HMAC_GENERAL		(0x212)
++#define CKM_SHA_1			(0x220)
++#define CKM_SHA_1_HMAC			(0x221)
++#define CKM_SHA_1_HMAC_GENERAL		(0x222)
++#define CKM_RIPEMD128			(0x230)
++#define CKM_RIPEMD128_HMAC		(0x231)
++#define CKM_RIPEMD128_HMAC_GENERAL	(0x232)
++#define CKM_RIPEMD160			(0x240)
++#define CKM_RIPEMD160_HMAC		(0x241)
++#define CKM_RIPEMD160_HMAC_GENERAL	(0x242)
++#define CKM_SHA256			(0x250)
++#define CKM_SHA256_HMAC			(0x251)
++#define CKM_SHA256_HMAC_GENERAL		(0x252)
++#define CKM_SHA384			(0x260)
++#define CKM_SHA384_HMAC			(0x261)
++#define CKM_SHA384_HMAC_GENERAL		(0x262)
++#define CKM_SHA512			(0x270)
++#define CKM_SHA512_HMAC			(0x271)
++#define CKM_SHA512_HMAC_GENERAL		(0x272)
++#define CKM_CAST_KEY_GEN		(0x300)
++#define CKM_CAST_ECB			(0x301)
++#define CKM_CAST_CBC			(0x302)
++#define CKM_CAST_MAC			(0x303)
++#define CKM_CAST_MAC_GENERAL		(0x304)
++#define CKM_CAST_CBC_PAD		(0x305)
++#define CKM_CAST3_KEY_GEN		(0x310)
++#define CKM_CAST3_ECB			(0x311)
++#define CKM_CAST3_CBC			(0x312)
++#define CKM_CAST3_MAC			(0x313)
++#define CKM_CAST3_MAC_GENERAL		(0x314)
++#define CKM_CAST3_CBC_PAD		(0x315)
++#define CKM_CAST5_KEY_GEN		(0x320)
++#define CKM_CAST128_KEY_GEN		(0x320)
++#define CKM_CAST5_ECB			(0x321)
++#define CKM_CAST128_ECB			(0x321)
++#define CKM_CAST5_CBC			(0x322)
++#define CKM_CAST128_CBC			(0x322)
++#define CKM_CAST5_MAC			(0x323)
++#define	CKM_CAST128_MAC			(0x323)
++#define CKM_CAST5_MAC_GENERAL		(0x324)
++#define CKM_CAST128_MAC_GENERAL		(0x324)
++#define CKM_CAST5_CBC_PAD		(0x325)
++#define CKM_CAST128_CBC_PAD		(0x325)
++#define CKM_RC5_KEY_GEN			(0x330)
++#define CKM_RC5_ECB			(0x331)
++#define CKM_RC5_CBC			(0x332)
++#define CKM_RC5_MAC			(0x333)
++#define CKM_RC5_MAC_GENERAL		(0x334)
++#define CKM_RC5_CBC_PAD			(0x335)
++#define CKM_IDEA_KEY_GEN		(0x340)
++#define CKM_IDEA_ECB			(0x341)
++#define	CKM_IDEA_CBC			(0x342)
++#define CKM_IDEA_MAC			(0x343)
++#define CKM_IDEA_MAC_GENERAL		(0x344)
++#define CKM_IDEA_CBC_PAD		(0x345)
++#define CKM_GENERIC_SECRET_KEY_GEN	(0x350)
++#define CKM_CONCATENATE_BASE_AND_KEY	(0x360)
++#define CKM_CONCATENATE_BASE_AND_DATA	(0x362)
++#define CKM_CONCATENATE_DATA_AND_BASE	(0x363)
++#define CKM_XOR_BASE_AND_DATA		(0x364)
++#define CKM_EXTRACT_KEY_FROM_KEY	(0x365)
++#define CKM_SSL3_PRE_MASTER_KEY_GEN	(0x370)
++#define CKM_SSL3_MASTER_KEY_DERIVE	(0x371)
++#define CKM_SSL3_KEY_AND_MAC_DERIVE	(0x372)
++#define CKM_SSL3_MASTER_KEY_DERIVE_DH	(0x373)
++#define CKM_TLS_PRE_MASTER_KEY_GEN	(0x374)
++#define CKM_TLS_MASTER_KEY_DERIVE	(0x375)
++#define CKM_TLS_KEY_AND_MAC_DERIVE	(0x376)
++#define CKM_TLS_MASTER_KEY_DERIVE_DH	(0x377)
++#define CKM_SSL3_MD5_MAC		(0x380)
++#define CKM_SSL3_SHA1_MAC		(0x381)
++#define CKM_MD5_KEY_DERIVATION		(0x390)
++#define CKM_MD2_KEY_DERIVATION		(0x391)
++#define CKM_SHA1_KEY_DERIVATION		(0x392)
++#define CKM_PBE_MD2_DES_CBC		(0x3a0)
++#define CKM_PBE_MD5_DES_CBC		(0x3a1)
++#define CKM_PBE_MD5_CAST_CBC		(0x3a2)
++#define CKM_PBE_MD5_CAST3_CBC		(0x3a3)
++#define CKM_PBE_MD5_CAST5_CBC		(0x3a4)
++#define CKM_PBE_MD5_CAST128_CBC		(0x3a4)
++#define CKM_PBE_SHA1_CAST5_CBC		(0x3a5)
++#define CKM_PBE_SHA1_CAST128_CBC	(0x3a5)
++#define CKM_PBE_SHA1_RC4_128		(0x3a6)
++#define CKM_PBE_SHA1_RC4_40		(0x3a7)
++#define CKM_PBE_SHA1_DES3_EDE_CBC	(0x3a8)
++#define CKM_PBE_SHA1_DES2_EDE_CBC	(0x3a9)
++#define CKM_PBE_SHA1_RC2_128_CBC	(0x3aa)
++#define CKM_PBE_SHA1_RC2_40_CBC		(0x3ab)
++#define CKM_PKCS5_PBKD2			(0x3b0)
++#define CKM_PBA_SHA1_WITH_SHA1_HMAC	(0x3c0)
++#define CKM_KEY_WRAP_LYNKS		(0x400)
++#define CKM_KEY_WRAP_SET_OAEP		(0x401)
++#define CKM_SKIPJACK_KEY_GEN		(0x1000)
++#define CKM_SKIPJACK_ECB64		(0x1001)
++#define CKM_SKIPJACK_CBC64		(0x1002)
++#define CKM_SKIPJACK_OFB64		(0x1003)
++#define CKM_SKIPJACK_CFB64		(0x1004)
++#define CKM_SKIPJACK_CFB32		(0x1005)
++#define CKM_SKIPJACK_CFB16		(0x1006)
++#define CKM_SKIPJACK_CFB8		(0x1007)
++#define CKM_SKIPJACK_WRAP		(0x1008)
++#define CKM_SKIPJACK_PRIVATE_WRAP	(0x1009)
++#define CKM_SKIPJACK_RELAYX		(0x100a)
++#define CKM_KEA_KEY_PAIR_GEN		(0x1010)
++#define CKM_KEA_KEY_DERIVE		(0x1011)
++#define CKM_FORTEZZA_TIMESTAMP		(0x1020)
++#define CKM_BATON_KEY_GEN		(0x1030)
++#define CKM_BATON_ECB128		(0x1031)
++#define CKM_BATON_ECB96			(0x1032)
++#define CKM_BATON_CBC128		(0x1033)
++#define CKM_BATON_COUNTER		(0x1034)
++#define CKM_BATON_SHUFFLE		(0x1035)
++#define CKM_BATON_WRAP			(0x1036)
++#define CKM_ECDSA_KEY_PAIR_GEN		(0x1040)
++#define CKM_EC_KEY_PAIR_GEN		(0x1040)
++#define CKM_ECDSA			(0x1041)
++#define CKM_ECDSA_SHA1			(0x1042)
++#define CKM_ECDH1_DERIVE		(0x1050)
++#define CKM_ECDH1_COFACTOR_DERIVE	(0x1051)
++#define CKM_ECMQV_DERIVE		(0x1052)
++#define CKM_JUNIPER_KEY_GEN		(0x1060)
++#define CKM_JUNIPER_ECB128		(0x1061)
++#define CKM_JUNIPER_CBC128		(0x1062)
++#define CKM_JUNIPER_COUNTER		(0x1063)
++#define CKM_JUNIPER_SHUFFLE		(0x1064)
++#define CKM_JUNIPER_WRAP		(0x1065)
++#define CKM_FASTHASH			(0x1070)
++#define CKM_AES_KEY_GEN			(0x1080)
++#define CKM_AES_ECB			(0x1081)
++#define CKM_AES_CBC			(0x1082)
++#define CKM_AES_MAC			(0x1083)
++#define CKM_AES_MAC_GENERAL		(0x1084)
++#define CKM_AES_CBC_PAD			(0x1085)
++#define CKM_DSA_PARAMETER_GEN		(0x2000)
++#define CKM_DH_PKCS_PARAMETER_GEN	(0x2001)
++#define CKM_X9_42_DH_PARAMETER_GEN	(0x2002)
++#define CKM_VENDOR_DEFINED		((unsigned long) (1 << 31))
++
++
++struct ck_mechanism
++{
++  ck_mechanism_type_t mechanism;
++  void *parameter;
++  unsigned long parameter_len;
++};
++
++
++struct ck_mechanism_info
++{
++  unsigned long min_key_size;
++  unsigned long max_key_size;
++  ck_flags_t flags;
++};
++
++#define CKF_HW			(1 << 0)
++#define CKF_ENCRYPT		(1 << 8)
++#define CKF_DECRYPT		(1 << 9)
++#define CKF_DIGEST		(1 << 10)
++#define CKF_SIGN		(1 << 11)
++#define CKF_SIGN_RECOVER	(1 << 12)
++#define CKF_VERIFY		(1 << 13)
++#define CKF_VERIFY_RECOVER	(1 << 14)
++#define CKF_GENERATE		(1 << 15)
++#define CKF_GENERATE_KEY_PAIR	(1 << 16)
++#define CKF_WRAP		(1 << 17)
++#define CKF_UNWRAP		(1 << 18)
++#define CKF_DERIVE		(1 << 19)
++#define CKF_EXTENSION		((unsigned long) (1 << 31))
++
++
++/* Flags for C_WaitForSlotEvent.  */
++#define CKF_DONT_BLOCK				(1)
++
++
++typedef unsigned long ck_rv_t;
++
++
++typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session,
++				ck_notification_t event, void *application);
++
++/* Forward reference.  */
++struct ck_function_list;
++
++#define _CK_DECLARE_FUNCTION(name, args)	\
++typedef ck_rv_t (*CK_ ## name) args;		\
++ck_rv_t CK_SPEC name args
++
++_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args));
++_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved));
++_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info));
++_CK_DECLARE_FUNCTION (C_GetFunctionList,
++		      (struct ck_function_list **function_list));
++
++_CK_DECLARE_FUNCTION (C_GetSlotList,
++		      (unsigned char token_present, ck_slot_id_t *slot_list,
++		       unsigned long *count));
++_CK_DECLARE_FUNCTION (C_GetSlotInfo,
++		      (ck_slot_id_t slot_id, struct ck_slot_info *info));
++_CK_DECLARE_FUNCTION (C_GetTokenInfo,
++		      (ck_slot_id_t slot_id, struct ck_token_info *info));
++_CK_DECLARE_FUNCTION (C_WaitForSlotEvent,
++		      (ck_flags_t flags, ck_slot_id_t *slot, void *reserved));
++_CK_DECLARE_FUNCTION (C_GetMechanismList,
++		      (ck_slot_id_t slot_id,
++		       ck_mechanism_type_t *mechanism_list,
++		       unsigned long *count));
++_CK_DECLARE_FUNCTION (C_GetMechanismInfo,
++		      (ck_slot_id_t slot_id, ck_mechanism_type_t type,
++		       struct ck_mechanism_info *info));
++_CK_DECLARE_FUNCTION (C_InitToken,
++		      (ck_slot_id_t slot_id, unsigned char *pin,
++		       unsigned long pin_len, unsigned char *label));
++_CK_DECLARE_FUNCTION (C_InitPIN,
++		      (ck_session_handle_t session, unsigned char *pin,
++		       unsigned long pin_len));
++_CK_DECLARE_FUNCTION (C_SetPIN,
++		      (ck_session_handle_t session, unsigned char *old_pin,
++		       unsigned long old_len, unsigned char *new_pin,
++		       unsigned long new_len));
++
++_CK_DECLARE_FUNCTION (C_OpenSession,
++		      (ck_slot_id_t slot_id, ck_flags_t flags,
++		       void *application, ck_notify_t notify,
++		       ck_session_handle_t *session));
++_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session));
++_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id));
++_CK_DECLARE_FUNCTION (C_GetSessionInfo,
++		      (ck_session_handle_t session,
++		       struct ck_session_info *info));
++_CK_DECLARE_FUNCTION (C_GetOperationState,
++		      (ck_session_handle_t session,
++		       unsigned char *operation_state,
++		       unsigned long *operation_state_len));
++_CK_DECLARE_FUNCTION (C_SetOperationState,
++		      (ck_session_handle_t session,
++		       unsigned char *operation_state,
++		       unsigned long operation_state_len,
++		       ck_object_handle_t encryption_key,
++		       ck_object_handle_t authentiation_key));
++_CK_DECLARE_FUNCTION (C_Login,
++		      (ck_session_handle_t session, ck_user_type_t user_type,
++		       unsigned char *pin, unsigned long pin_len));
++_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session));
++
++_CK_DECLARE_FUNCTION (C_CreateObject,
++		      (ck_session_handle_t session,
++		       struct ck_attribute *templ,
++		       unsigned long count, ck_object_handle_t *object));
++_CK_DECLARE_FUNCTION (C_CopyObject,
++		      (ck_session_handle_t session, ck_object_handle_t object,
++		       struct ck_attribute *templ, unsigned long count,
++		       ck_object_handle_t *new_object));
++_CK_DECLARE_FUNCTION (C_DestroyObject,
++		      (ck_session_handle_t session,
++		       ck_object_handle_t object));
++_CK_DECLARE_FUNCTION (C_GetObjectSize,
++		      (ck_session_handle_t session,
++		       ck_object_handle_t object,
++		       unsigned long *size));
++_CK_DECLARE_FUNCTION (C_GetAttributeValue,
++		      (ck_session_handle_t session,
++		       ck_object_handle_t object,
++		       struct ck_attribute *templ,
++		       unsigned long count));
++_CK_DECLARE_FUNCTION (C_SetAttributeValue,
++		      (ck_session_handle_t session,
++		       ck_object_handle_t object,
++		       struct ck_attribute *templ,
++		       unsigned long count));
++_CK_DECLARE_FUNCTION (C_FindObjectsInit,
++		      (ck_session_handle_t session,
++		       struct ck_attribute *templ,
++		       unsigned long count));
++_CK_DECLARE_FUNCTION (C_FindObjects,
++		      (ck_session_handle_t session,
++		       ck_object_handle_t *object,
++		       unsigned long max_object_count,
++		       unsigned long *object_count));
++_CK_DECLARE_FUNCTION (C_FindObjectsFinal,
++		      (ck_session_handle_t session));
++
++_CK_DECLARE_FUNCTION (C_EncryptInit,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t key));
++_CK_DECLARE_FUNCTION (C_Encrypt,
++		      (ck_session_handle_t session,
++		       unsigned char *data, unsigned long data_len,
++		       unsigned char *encrypted_data,
++		       unsigned long *encrypted_data_len));
++_CK_DECLARE_FUNCTION (C_EncryptUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *part, unsigned long part_len,
++		       unsigned char *encrypted_part,
++		       unsigned long *encrypted_part_len));
++_CK_DECLARE_FUNCTION (C_EncryptFinal,
++		      (ck_session_handle_t session,
++		       unsigned char *last_encrypted_part,
++		       unsigned long *last_encrypted_part_len));
++
++_CK_DECLARE_FUNCTION (C_DecryptInit,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t key));
++_CK_DECLARE_FUNCTION (C_Decrypt,
++		      (ck_session_handle_t session,
++		       unsigned char *encrypted_data,
++		       unsigned long encrypted_data_len,
++		       unsigned char *data, unsigned long *data_len));
++_CK_DECLARE_FUNCTION (C_DecryptUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *encrypted_part,
++		       unsigned long encrypted_part_len,
++		       unsigned char *part, unsigned long *part_len));
++_CK_DECLARE_FUNCTION (C_DecryptFinal,
++		      (ck_session_handle_t session,
++		       unsigned char *last_part,
++		       unsigned long *last_part_len));
++
++_CK_DECLARE_FUNCTION (C_DigestInit,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism));
++_CK_DECLARE_FUNCTION (C_Digest,
++		      (ck_session_handle_t session,
++		       unsigned char *data, unsigned long data_len,
++		       unsigned char *digest,
++		       unsigned long *digest_len));
++_CK_DECLARE_FUNCTION (C_DigestUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *part, unsigned long part_len));
++_CK_DECLARE_FUNCTION (C_DigestKey,
++		      (ck_session_handle_t session, ck_object_handle_t key));
++_CK_DECLARE_FUNCTION (C_DigestFinal,
++		      (ck_session_handle_t session,
++		       unsigned char *digest,
++		       unsigned long *digest_len));
++
++_CK_DECLARE_FUNCTION (C_SignInit,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t key));
++_CK_DECLARE_FUNCTION (C_Sign,
++		      (ck_session_handle_t session,
++		       unsigned char *data, unsigned long data_len,
++		       unsigned char *signature,
++		       unsigned long *signature_len));
++_CK_DECLARE_FUNCTION (C_SignUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *part, unsigned long part_len));
++_CK_DECLARE_FUNCTION (C_SignFinal,
++		      (ck_session_handle_t session,
++		       unsigned char *signature,
++		       unsigned long *signature_len));
++_CK_DECLARE_FUNCTION (C_SignRecoverInit,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t key));
++_CK_DECLARE_FUNCTION (C_SignRecover,
++		      (ck_session_handle_t session,
++		       unsigned char *data, unsigned long data_len,
++		       unsigned char *signature,
++		       unsigned long *signature_len));
++
++_CK_DECLARE_FUNCTION (C_VerifyInit,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t key));
++_CK_DECLARE_FUNCTION (C_Verify,
++		      (ck_session_handle_t session,
++		       unsigned char *data, unsigned long data_len,
++		       unsigned char *signature,
++		       unsigned long signature_len));
++_CK_DECLARE_FUNCTION (C_VerifyUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *part, unsigned long part_len));
++_CK_DECLARE_FUNCTION (C_VerifyFinal,
++		      (ck_session_handle_t session,
++		       unsigned char *signature,
++		       unsigned long signature_len));
++_CK_DECLARE_FUNCTION (C_VerifyRecoverInit,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t key));
++_CK_DECLARE_FUNCTION (C_VerifyRecover,
++		      (ck_session_handle_t session,
++		       unsigned char *signature,
++		       unsigned long signature_len,
++		       unsigned char *data,
++		       unsigned long *data_len));
++
++_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *part, unsigned long part_len,
++		       unsigned char *encrypted_part,
++		       unsigned long *encrypted_part_len));
++_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *encrypted_part,
++		       unsigned long encrypted_part_len,
++		       unsigned char *part,
++		       unsigned long *part_len));
++_CK_DECLARE_FUNCTION (C_SignEncryptUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *part, unsigned long part_len,
++		       unsigned char *encrypted_part,
++		       unsigned long *encrypted_part_len));
++_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate,
++		      (ck_session_handle_t session,
++		       unsigned char *encrypted_part,
++		       unsigned long encrypted_part_len,
++		       unsigned char *part,
++		       unsigned long *part_len));
++
++_CK_DECLARE_FUNCTION (C_GenerateKey,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       struct ck_attribute *templ,
++		       unsigned long count,
++		       ck_object_handle_t *key));
++_CK_DECLARE_FUNCTION (C_GenerateKeyPair,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       struct ck_attribute *public_key_template,
++		       unsigned long public_key_attribute_count,
++		       struct ck_attribute *private_key_template,
++		       unsigned long private_key_attribute_count,
++		       ck_object_handle_t *public_key,
++		       ck_object_handle_t *private_key));
++_CK_DECLARE_FUNCTION (C_WrapKey,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t wrapping_key,
++		       ck_object_handle_t key,
++		       unsigned char *wrapped_key,
++		       unsigned long *wrapped_key_len));
++_CK_DECLARE_FUNCTION (C_UnwrapKey,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t unwrapping_key,
++		       unsigned char *wrapped_key,
++		       unsigned long wrapped_key_len,
++		       struct ck_attribute *templ,
++		       unsigned long attribute_count,
++		       ck_object_handle_t *key));
++_CK_DECLARE_FUNCTION (C_DeriveKey,
++		      (ck_session_handle_t session,
++		       struct ck_mechanism *mechanism,
++		       ck_object_handle_t base_key,
++		       struct ck_attribute *templ,
++		       unsigned long attribute_count,
++		       ck_object_handle_t *key));
++
++_CK_DECLARE_FUNCTION (C_SeedRandom,
++		      (ck_session_handle_t session, unsigned char *seed,
++		       unsigned long seed_len));
++_CK_DECLARE_FUNCTION (C_GenerateRandom,
++		      (ck_session_handle_t session,
++		       unsigned char *random_data,
++		       unsigned long random_len));
++
++_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session));
++_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session));
++
++
++struct ck_function_list
++{
++  struct ck_version version;
++  CK_C_Initialize C_Initialize;
++  CK_C_Finalize C_Finalize;
++  CK_C_GetInfo C_GetInfo;
++  CK_C_GetFunctionList C_GetFunctionList;
++  CK_C_GetSlotList C_GetSlotList;
++  CK_C_GetSlotInfo C_GetSlotInfo;
++  CK_C_GetTokenInfo C_GetTokenInfo;
++  CK_C_GetMechanismList C_GetMechanismList;
++  CK_C_GetMechanismInfo C_GetMechanismInfo;
++  CK_C_InitToken C_InitToken;
++  CK_C_InitPIN C_InitPIN;
++  CK_C_SetPIN C_SetPIN;
++  CK_C_OpenSession C_OpenSession;
++  CK_C_CloseSession C_CloseSession;
++  CK_C_CloseAllSessions C_CloseAllSessions;
++  CK_C_GetSessionInfo C_GetSessionInfo;
++  CK_C_GetOperationState C_GetOperationState;
++  CK_C_SetOperationState C_SetOperationState;
++  CK_C_Login C_Login;
++  CK_C_Logout C_Logout;
++  CK_C_CreateObject C_CreateObject;
++  CK_C_CopyObject C_CopyObject;
++  CK_C_DestroyObject C_DestroyObject;
++  CK_C_GetObjectSize C_GetObjectSize;
++  CK_C_GetAttributeValue C_GetAttributeValue;
++  CK_C_SetAttributeValue C_SetAttributeValue;
++  CK_C_FindObjectsInit C_FindObjectsInit;
++  CK_C_FindObjects C_FindObjects;
++  CK_C_FindObjectsFinal C_FindObjectsFinal;
++  CK_C_EncryptInit C_EncryptInit;
++  CK_C_Encrypt C_Encrypt;
++  CK_C_EncryptUpdate C_EncryptUpdate;
++  CK_C_EncryptFinal C_EncryptFinal;
++  CK_C_DecryptInit C_DecryptInit;
++  CK_C_Decrypt C_Decrypt;
++  CK_C_DecryptUpdate C_DecryptUpdate;
++  CK_C_DecryptFinal C_DecryptFinal;
++  CK_C_DigestInit C_DigestInit;
++  CK_C_Digest C_Digest;
++  CK_C_DigestUpdate C_DigestUpdate;
++  CK_C_DigestKey C_DigestKey;
++  CK_C_DigestFinal C_DigestFinal;
++  CK_C_SignInit C_SignInit;
++  CK_C_Sign C_Sign;
++  CK_C_SignUpdate C_SignUpdate;
++  CK_C_SignFinal C_SignFinal;
++  CK_C_SignRecoverInit C_SignRecoverInit;
++  CK_C_SignRecover C_SignRecover;
++  CK_C_VerifyInit C_VerifyInit;
++  CK_C_Verify C_Verify;
++  CK_C_VerifyUpdate C_VerifyUpdate;
++  CK_C_VerifyFinal C_VerifyFinal;
++  CK_C_VerifyRecoverInit C_VerifyRecoverInit;
++  CK_C_VerifyRecover C_VerifyRecover;
++  CK_C_DigestEncryptUpdate C_DigestEncryptUpdate;
++  CK_C_DecryptDigestUpdate C_DecryptDigestUpdate;
++  CK_C_SignEncryptUpdate C_SignEncryptUpdate;
++  CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate;
++  CK_C_GenerateKey C_GenerateKey;
++  CK_C_GenerateKeyPair C_GenerateKeyPair;
++  CK_C_WrapKey C_WrapKey;
++  CK_C_UnwrapKey C_UnwrapKey;
++  CK_C_DeriveKey C_DeriveKey;
++  CK_C_SeedRandom C_SeedRandom;
++  CK_C_GenerateRandom C_GenerateRandom;
++  CK_C_GetFunctionStatus C_GetFunctionStatus;
++  CK_C_CancelFunction C_CancelFunction;
++  CK_C_WaitForSlotEvent C_WaitForSlotEvent;
++};
++
++
++typedef ck_rv_t (*ck_createmutex_t) (void **mutex);
++typedef ck_rv_t (*ck_destroymutex_t) (void *mutex);
++typedef ck_rv_t (*ck_lockmutex_t) (void *mutex);
++typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex);
++
++
++struct ck_c_initialize_args
++{
++  ck_createmutex_t create_mutex;
++  ck_destroymutex_t destroy_mutex;
++  ck_lockmutex_t lock_mutex;
++  ck_unlockmutex_t unlock_mutex;
++  ck_flags_t flags;
++  void *reserved;
++};
++
++
++#define CKF_LIBRARY_CANT_CREATE_OS_THREADS	(1 << 0)
++#define CKF_OS_LOCKING_OK			(1 << 1)
++
++#define CKR_OK					(0)
++#define CKR_CANCEL				(1)
++#define CKR_HOST_MEMORY				(2)
++#define CKR_SLOT_ID_INVALID			(3)
++#define CKR_GENERAL_ERROR			(5)
++#define CKR_FUNCTION_FAILED			(6)
++#define CKR_ARGUMENTS_BAD			(7)
++#define CKR_NO_EVENT				(8)
++#define CKR_NEED_TO_CREATE_THREADS		(9)
++#define CKR_CANT_LOCK				(0xa)
++#define CKR_ATTRIBUTE_READ_ONLY			(0x10)
++#define CKR_ATTRIBUTE_SENSITIVE			(0x11)
++#define CKR_ATTRIBUTE_TYPE_INVALID		(0x12)
++#define CKR_ATTRIBUTE_VALUE_INVALID		(0x13)
++#define CKR_DATA_INVALID			(0x20)
++#define CKR_DATA_LEN_RANGE			(0x21)
++#define CKR_DEVICE_ERROR			(0x30)
++#define CKR_DEVICE_MEMORY			(0x31)
++#define CKR_DEVICE_REMOVED			(0x32)
++#define CKR_ENCRYPTED_DATA_INVALID		(0x40)
++#define CKR_ENCRYPTED_DATA_LEN_RANGE		(0x41)
++#define CKR_FUNCTION_CANCELED			(0x50)
++#define CKR_FUNCTION_NOT_PARALLEL		(0x51)
++#define CKR_FUNCTION_NOT_SUPPORTED		(0x54)
++#define CKR_KEY_HANDLE_INVALID			(0x60)
++#define CKR_KEY_SIZE_RANGE			(0x62)
++#define CKR_KEY_TYPE_INCONSISTENT		(0x63)
++#define CKR_KEY_NOT_NEEDED			(0x64)
++#define CKR_KEY_CHANGED				(0x65)
++#define CKR_KEY_NEEDED				(0x66)
++#define CKR_KEY_INDIGESTIBLE			(0x67)
++#define CKR_KEY_FUNCTION_NOT_PERMITTED		(0x68)
++#define CKR_KEY_NOT_WRAPPABLE			(0x69)
++#define CKR_KEY_UNEXTRACTABLE			(0x6a)
++#define CKR_MECHANISM_INVALID			(0x70)
++#define CKR_MECHANISM_PARAM_INVALID		(0x71)
++#define CKR_OBJECT_HANDLE_INVALID		(0x82)
++#define CKR_OPERATION_ACTIVE			(0x90)
++#define CKR_OPERATION_NOT_INITIALIZED		(0x91)
++#define CKR_PIN_INCORRECT			(0xa0)
++#define CKR_PIN_INVALID				(0xa1)
++#define CKR_PIN_LEN_RANGE			(0xa2)
++#define CKR_PIN_EXPIRED				(0xa3)
++#define CKR_PIN_LOCKED				(0xa4)
++#define CKR_SESSION_CLOSED			(0xb0)
++#define CKR_SESSION_COUNT			(0xb1)
++#define CKR_SESSION_HANDLE_INVALID		(0xb3)
++#define CKR_SESSION_PARALLEL_NOT_SUPPORTED	(0xb4)
++#define CKR_SESSION_READ_ONLY			(0xb5)
++#define CKR_SESSION_EXISTS			(0xb6)
++#define CKR_SESSION_READ_ONLY_EXISTS		(0xb7)
++#define CKR_SESSION_READ_WRITE_SO_EXISTS	(0xb8)
++#define CKR_SIGNATURE_INVALID			(0xc0)
++#define CKR_SIGNATURE_LEN_RANGE			(0xc1)
++#define CKR_TEMPLATE_INCOMPLETE			(0xd0)
++#define CKR_TEMPLATE_INCONSISTENT		(0xd1)
++#define CKR_TOKEN_NOT_PRESENT			(0xe0)
++#define CKR_TOKEN_NOT_RECOGNIZED		(0xe1)
++#define CKR_TOKEN_WRITE_PROTECTED		(0xe2)
++#define	CKR_UNWRAPPING_KEY_HANDLE_INVALID	(0xf0)
++#define CKR_UNWRAPPING_KEY_SIZE_RANGE		(0xf1)
++#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT	(0xf2)
++#define CKR_USER_ALREADY_LOGGED_IN		(0x100)
++#define CKR_USER_NOT_LOGGED_IN			(0x101)
++#define CKR_USER_PIN_NOT_INITIALIZED		(0x102)
++#define CKR_USER_TYPE_INVALID			(0x103)
++#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN	(0x104)
++#define CKR_USER_TOO_MANY_TYPES			(0x105)
++#define CKR_WRAPPED_KEY_INVALID			(0x110)
++#define CKR_WRAPPED_KEY_LEN_RANGE		(0x112)
++#define CKR_WRAPPING_KEY_HANDLE_INVALID		(0x113)
++#define CKR_WRAPPING_KEY_SIZE_RANGE		(0x114)
++#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT	(0x115)
++#define CKR_RANDOM_SEED_NOT_SUPPORTED		(0x120)
++#define CKR_RANDOM_NO_RNG			(0x121)
++#define CKR_DOMAIN_PARAMS_INVALID		(0x130)
++#define CKR_BUFFER_TOO_SMALL			(0x150)
++#define CKR_SAVED_STATE_INVALID			(0x160)
++#define CKR_INFORMATION_SENSITIVE		(0x170)
++#define CKR_STATE_UNSAVEABLE			(0x180)
++#define CKR_CRYPTOKI_NOT_INITIALIZED		(0x190)
++#define CKR_CRYPTOKI_ALREADY_INITIALIZED	(0x191)
++#define CKR_MUTEX_BAD				(0x1a0)
++#define CKR_MUTEX_NOT_LOCKED			(0x1a1)
++#define CKR_FUNCTION_REJECTED			(0x200)
++#define CKR_VENDOR_DEFINED			((unsigned long) (1 << 31))
++
++
++
++/* Compatibility layer.  */
++
++#ifdef CRYPTOKI_COMPAT
++
++#undef CK_DEFINE_FUNCTION
++#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name
++
++/* For NULL.  */
++#include <stddef.h>
++
++typedef unsigned char CK_BYTE;
++typedef unsigned char CK_CHAR;
++typedef unsigned char CK_UTF8CHAR;
++typedef unsigned char CK_BBOOL;
++typedef unsigned long int CK_ULONG;
++typedef long int CK_LONG;
++typedef CK_BYTE *CK_BYTE_PTR;
++typedef CK_CHAR *CK_CHAR_PTR;
++typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR;
++typedef CK_ULONG *CK_ULONG_PTR;
++typedef void *CK_VOID_PTR;
++typedef void **CK_VOID_PTR_PTR;
++#define CK_FALSE 0
++#define CK_TRUE 1
++#ifndef CK_DISABLE_TRUE_FALSE
++#ifndef FALSE
++#define FALSE 0
++#endif
++#ifndef TRUE
++#define TRUE 1
++#endif
++#endif
++
++typedef struct ck_version CK_VERSION;
++typedef struct ck_version *CK_VERSION_PTR;
++
++typedef struct ck_info CK_INFO;
++typedef struct ck_info *CK_INFO_PTR;
++
++typedef ck_slot_id_t *CK_SLOT_ID_PTR;
++
++typedef struct ck_slot_info CK_SLOT_INFO;
++typedef struct ck_slot_info *CK_SLOT_INFO_PTR;
++
++typedef struct ck_token_info CK_TOKEN_INFO;
++typedef struct ck_token_info *CK_TOKEN_INFO_PTR;
++
++typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR;
++
++typedef struct ck_session_info CK_SESSION_INFO;
++typedef struct ck_session_info *CK_SESSION_INFO_PTR;
++
++typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR;
++
++typedef ck_object_class_t *CK_OBJECT_CLASS_PTR;
++
++typedef struct ck_attribute CK_ATTRIBUTE;
++typedef struct ck_attribute *CK_ATTRIBUTE_PTR;
++
++typedef struct ck_date CK_DATE;
++typedef struct ck_date *CK_DATE_PTR;
++
++typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR;
++
++typedef struct ck_mechanism CK_MECHANISM;
++typedef struct ck_mechanism *CK_MECHANISM_PTR;
++
++typedef struct ck_mechanism_info CK_MECHANISM_INFO;
++typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR;
++
++typedef struct ck_function_list CK_FUNCTION_LIST;
++typedef struct ck_function_list *CK_FUNCTION_LIST_PTR;
++typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR;
++
++typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS;
++typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR;
++
++#define NULL_PTR NULL
++
++/* Delete the helper macros defined at the top of the file.  */
++#undef ck_flags_t
++#undef ck_version
++
++#undef ck_info
++#undef cryptoki_version
++#undef manufacturer_id
++#undef library_description
++#undef library_version
++
++#undef ck_notification_t
++#undef ck_slot_id_t
++
++#undef ck_slot_info
++#undef slot_description
++#undef hardware_version
++#undef firmware_version
++
++#undef ck_token_info
++#undef serial_number
++#undef max_session_count
++#undef session_count
++#undef max_rw_session_count
++#undef rw_session_count
++#undef max_pin_len
++#undef min_pin_len
++#undef total_public_memory
++#undef free_public_memory
++#undef total_private_memory
++#undef free_private_memory
++#undef utc_time
++
++#undef ck_session_handle_t
++#undef ck_user_type_t
++#undef ck_state_t
++
++#undef ck_session_info
++#undef slot_id
++#undef device_error
++
++#undef ck_object_handle_t
++#undef ck_object_class_t
++#undef ck_hw_feature_type_t
++#undef ck_key_type_t
++#undef ck_certificate_type_t
++#undef ck_attribute_type_t
++
++#undef ck_attribute
++#undef value
++#undef value_len
++
++#undef ck_date
++
++#undef ck_mechanism_type_t
++
++#undef ck_mechanism
++#undef parameter
++#undef parameter_len
++
++#undef ck_mechanism_info
++#undef min_key_size
++#undef max_key_size
++
++#undef ck_rv_t
++#undef ck_notify_t
++
++#undef ck_function_list
++
++#undef ck_createmutex_t
++#undef ck_destroymutex_t
++#undef ck_lockmutex_t
++#undef ck_unlockmutex_t
++
++#undef ck_c_initialize_args
++#undef create_mutex
++#undef destroy_mutex
++#undef lock_mutex
++#undef unlock_mutex
++#undef reserved
++
++#endif	/* CRYPTOKI_COMPAT */
++
++
++/* System dependencies.  */
++#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
++#pragma pack(pop, cryptoki)
++#endif
++
++#if defined(__cplusplus)
++}
++#endif
++
++#endif	/* PKCS11_H */
+diff -up openssh-5.3p1/readconf.c.pkcs11 openssh-5.3p1/readconf.c
+--- openssh-5.3p1/readconf.c.pkcs11	2013-07-10 14:34:46.258673757 +0200
++++ openssh-5.3p1/readconf.c	2013-07-10 14:34:46.500673758 +0200
+@@ -123,7 +123,7 @@ typedef enum {
+ 	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
+ 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
+ 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
+-	oHostKeyAlgorithms, oBindAddress, oSmartcardDevice,
++	oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
+ 	oUseNSS, oNSSToken, oNSSModule,
+ 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
+ 	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
+@@ -215,10 +215,12 @@ static struct {
+ 	{ "preferredauthentications", oPreferredAuthentications },
+ 	{ "hostkeyalgorithms", oHostKeyAlgorithms },
+ 	{ "bindaddress", oBindAddress },
+-#ifdef SMARTCARD
+-	{ "smartcarddevice", oSmartcardDevice },
++#ifdef ENABLE_PKCS11
++	{ "smartcarddevice", oPKCS11Provider },
++	{ "pkcs11provider", oPKCS11Provider },
+ #else
+ 	{ "smartcarddevice", oUnsupported },
++	{ "pkcs11provider", oUnsupported },
+ #endif
+ #ifdef HAVE_LIBNSS
+ 	{ "usenss", oUseNSS },
+@@ -644,8 +646,8 @@ parse_string:
+ 		charptr = &options->bind_address;
+ 		goto parse_string;
+ 
+-	case oSmartcardDevice:
+-		charptr = &options->smartcard_device;
++	case oPKCS11Provider:
++		charptr = &options->pkcs11_provider;
+ 		goto parse_string;
+ 
+ 	case oUseNSS:
+@@ -1112,7 +1114,7 @@ initialize_options(Options * options)
+ 	options->log_level = SYSLOG_LEVEL_NOT_SET;
+ 	options->preferred_authentications = NULL;
+ 	options->bind_address = NULL;
+-	options->smartcard_device = NULL;
++	options->pkcs11_provider = NULL;
+ 	options->use_nss = -1;
+ 	options->nss_token = NULL;
+ 	options->num_nss_modules = 0;
+diff -up openssh-5.3p1/readconf.h.pkcs11 openssh-5.3p1/readconf.h
+--- openssh-5.3p1/readconf.h.pkcs11	2013-07-10 14:34:46.259673757 +0200
++++ openssh-5.3p1/readconf.h	2013-07-10 14:34:46.501673758 +0200
+@@ -88,7 +88,7 @@ typedef struct {
+ 	char   *user_hostfile2;
+ 	char   *preferred_authentications;
+ 	char   *bind_address;	/* local socket address for connection to sshd */
+-	char   *smartcard_device; /* Smartcard reader device */
++	char   *pkcs11_provider; /* PKCS#11 provider */
+ 	int     use_nss;        /* Use NSS library for keys */
+ 	char   *nss_token;      /* Look for NSS keys on token */
+ 	int     num_nss_modules; /* Number of PCKS#11 modules. */
+diff -up openssh-5.3p1/scp.1.pkcs11 openssh-5.3p1/scp.1
+--- openssh-5.3p1/scp.1.pkcs11	2013-07-10 14:34:46.421673756 +0200
++++ openssh-5.3p1/scp.1	2013-07-10 14:34:46.502673758 +0200
+@@ -161,6 +161,7 @@ For full details of the options listed b
+ .It NoHostAuthenticationForLocalhost
+ .It NumberOfPasswordPrompts
+ .It PasswordAuthentication
++.It PKCS11Provider
+ .It Port
+ .It PreferredAuthentications
+ .It Protocol
+@@ -172,7 +173,6 @@ For full details of the options listed b
+ .It SendEnv
+ .It ServerAliveInterval
+ .It ServerAliveCountMax
+-.It SmartcardDevice
+ .It StrictHostKeyChecking
+ .It TCPKeepAlive
+ .It UsePrivilegedPort
+diff -up openssh-5.3p1/sftp.1.pkcs11 openssh-5.3p1/sftp.1
+--- openssh-5.3p1/sftp.1.pkcs11	2013-07-10 14:34:46.422673756 +0200
++++ openssh-5.3p1/sftp.1	2013-07-10 14:34:46.503673758 +0200
+@@ -175,6 +175,7 @@ For full details of the options listed b
+ .It NoHostAuthenticationForLocalhost
+ .It NumberOfPasswordPrompts
+ .It PasswordAuthentication
++.It PKCS11Provider
+ .It Port
+ .It PreferredAuthentications
+ .It Protocol
+@@ -186,7 +187,6 @@ For full details of the options listed b
+ .It SendEnv
+ .It ServerAliveInterval
+ .It ServerAliveCountMax
+-.It SmartcardDevice
+ .It StrictHostKeyChecking
+ .It TCPKeepAlive
+ .It UsePrivilegedPort
+diff -up openssh-5.3p1/ssh.1.pkcs11 openssh-5.3p1/ssh.1
+--- openssh-5.3p1/ssh.1.pkcs11	2013-07-10 14:34:46.479673758 +0200
++++ openssh-5.3p1/ssh.1	2013-07-10 14:34:46.504673758 +0200
+@@ -54,6 +54,7 @@
+ .Oc
+ .Op Fl e Ar escape_char
+ .Op Fl F Ar configfile
++.Op Fl I Ar pkcs11
+ .Bk -words
+ .Op Fl i Ar identity_file
+ .Ek
+@@ -283,12 +284,12 @@ will wait for all remote port forwards t
+ before placing itself in the background.
+ .It Fl g
+ Allows remote hosts to connect to local forwarded ports.
+-.It Fl I Ar smartcard_device
+-Specify the device
++.It Fl I Ar pkcs11
++Specify the PKCS#11 shared libarary
+ .Nm
+-should use to communicate with a smartcard used for storing the user's
++should use to communicate with a PKCS#11 token used for storing the user's
+ private RSA key.
+-This option is only available if support for smartcard devices
++This option is only available if support for PKCS#11
+ is compiled in (default is no support).
+ .It Fl i Ar identity_file
+ Selects a file from which the identity (private key) for
+@@ -468,6 +469,7 @@ For full details of the options listed b
+ .It NumberOfPasswordPrompts
+ .It PasswordAuthentication
+ .It PermitLocalCommand
++.It PKCS11Provider
+ .It Port
+ .It PreferredAuthentications
+ .It Protocol
+@@ -480,7 +482,6 @@ For full details of the options listed b
+ .It SendEnv
+ .It ServerAliveInterval
+ .It ServerAliveCountMax
+-.It SmartcardDevice
+ .It StrictHostKeyChecking
+ .It TCPKeepAlive
+ .It Tunnel
+diff -up openssh-5.3p1/ssh-add.1.pkcs11 openssh-5.3p1/ssh-add.1
+--- openssh-5.3p1/ssh-add.1.pkcs11	2013-07-10 14:34:46.409673758 +0200
++++ openssh-5.3p1/ssh-add.1	2013-07-10 14:34:46.505673758 +0200
+@@ -49,9 +49,9 @@
+ .Op Fl t Ar life
+ .Op Ar
+ .Nm ssh-add
+-.Fl s Ar reader
++.Fl s Ar pkcs11
+ .Nm ssh-add
+-.Fl e Ar reader
++.Fl e Ar pkcs11
+ .Nm ssh-add
+ .Fl n
+ .Op Fl T Ar token
+@@ -104,17 +104,17 @@ If no public key is found at a given pat
+ will append
+ .Pa .pub
+ and retry.
+-.It Fl e Ar reader
+-Remove key in smartcard
+-.Ar reader .
++.It Fl e Ar pkcs11
++Remove key provided by
++.Ar pkcs11 .
+ .It Fl L
+ Lists public key parameters of all identities currently represented
+ by the agent.
+ .It Fl l
+ Lists fingerprints of all identities currently represented by the agent.
+-.It Fl s Ar reader
+-Add key in smartcard
+-.Ar reader .
++.It Fl s Ar pkcs11
++Add key provided by
++.Ar pkcs11 .
+ .It Fl t Ar life
+ Set a maximum lifetime when adding identities to an agent.
+ The lifetime may be specified in seconds or in a time format
+diff -up openssh-5.3p1/ssh-add.c.pkcs11 openssh-5.3p1/ssh-add.c
+--- openssh-5.3p1/ssh-add.c.pkcs11	2013-07-10 14:34:46.210673758 +0200
++++ openssh-5.3p1/ssh-add.c	2013-07-10 14:34:46.506673758 +0200
+@@ -224,7 +224,7 @@ update_card(AuthenticationConnection *ac
+ 	char *pin;
+ 	int ret = -1;
+ 
+-	pin = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN);
++	pin = read_passphrase("Enter passphrase for PKCS#11: ", RP_ALLOW_STDIN);
+ 	if (pin == NULL)
+ 		return -1;
+ 
+@@ -456,12 +456,10 @@ usage(void)
+ 	fprintf(stderr, "  -X          Unlock agent.\n");
+ 	fprintf(stderr, "  -t life     Set lifetime (in seconds) when adding identities.\n");
+ 	fprintf(stderr, "  -c          Require confirmation to sign using identities\n");
+-#ifdef SMARTCARD
+-	fprintf(stderr, "  -s reader   Add key in smartcard reader.\n");
+-	fprintf(stderr, "  -e reader   Remove key in smartcard reader.\n");
++	fprintf(stderr, "  -s pkcs11   Add keys from PKCS#11 provider.\n");
++	fprintf(stderr, "  -e pkcs11   Remove keys provided by PKCS#11 provider.\n");
+ 	fprintf(stderr, "  -n          Add keys in NSS key.\n");
+ 	fprintf(stderr, "  -T          Name NSS key explicitly.\n");
+-#endif
+ }
+ 
+ int
+@@ -470,7 +468,7 @@ main(int argc, char **argv)
+ 	extern char *optarg;
+ 	extern int optind;
+ 	AuthenticationConnection *ac = NULL;
+-	char *sc_reader_id = NULL;
++	char *pkcs11provider = NULL;
+ 	int i, ch, deleting = 0, ret = 0;
+ #ifdef HAVE_LIBNSS
+ 	char *token_id = NULL;
+@@ -521,11 +519,11 @@ main(int argc, char **argv)
+ 			break;
+ #endif
+ 		case 's':
+-			sc_reader_id = optarg;
++			pkcs11provider = optarg;
+ 			break;
+ 		case 'e':
+ 			deleting = 1;
+-			sc_reader_id = optarg;
++			pkcs11provider = optarg;
+ 			break;
+ 		case 't':
+ 			if ((lifetime = convtime(optarg)) == -1) {
+@@ -547,8 +545,8 @@ main(int argc, char **argv)
+ 	}
+ 	argc -= optind;
+ 	argv += optind;
+-	if (sc_reader_id != NULL) {
+-		if (update_card(ac, !deleting, sc_reader_id) == -1)
++	if (pkcs11provider != NULL) {
++		if (update_card(ac, !deleting, pkcs11provider) == -1)
+ 			ret = 1;
+ 		goto done;
+ 	}
+diff -up openssh-5.3p1/ssh-agent.c.pkcs11 openssh-5.3p1/ssh-agent.c
+--- openssh-5.3p1/ssh-agent.c.pkcs11	2013-07-10 14:34:46.487673758 +0200
++++ openssh-5.3p1/ssh-agent.c	2013-07-10 14:34:46.508673758 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: ssh-agent.c,v 1.162 2009/09/01 14:43:17 djm Exp $ */
++/* $OpenBSD: ssh-agent.c,v 1.164 2010/02/09 00:50:36 djm Exp $ */
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
+@@ -77,8 +77,8 @@
+ #include "log.h"
+ #include "misc.h"
+ 
+-#ifdef SMARTCARD
+-#include "scard.h"
++#ifdef ENABLE_PKCS11
++#include "ssh-pkcs11.h"
+ #endif
+ 
+ #ifdef HAVE_LIBNSS
+@@ -110,6 +110,7 @@ typedef struct identity {
+ 	TAILQ_ENTRY(identity) next;
+ 	Key *key;
+ 	char *comment;
++	char *provider;
+ 	u_int death;
+ 	u_int confirm;
+ } Identity;
+@@ -176,6 +177,8 @@ static void
+ free_identity(Identity *id)
+ {
+ 	key_free(id->key);
++	if (id->provider != NULL)
++		xfree(id->provider);
+ 	xfree(id->comment);
+ 	xfree(id);
+ }
+@@ -554,7 +557,7 @@ process_add_identity(SocketEntry *e, int
+ 	if (lifetime && !death)
+ 		death = time(NULL) + lifetime;
+ 	if ((id = lookup_identity(k, version)) == NULL) {
+-		id = xmalloc(sizeof(Identity));
++		id = xcalloc(1, sizeof(Identity));
+ 		id->key = k;
+ 		TAILQ_INSERT_TAIL(&tab->idlist, id, next);
+ 		/* Increment the number of identities. */
+@@ -614,17 +617,17 @@ no_identities(SocketEntry *e, u_int type
+ 	buffer_free(&msg);
+ }
+ 
+-#ifdef SMARTCARD
++#ifdef ENABLE_PKCS11
+ static void
+ process_add_smartcard_key(SocketEntry *e)
+ {
+-	char *sc_reader_id = NULL, *pin;
+-	int i, type, version, success = 0, death = 0, confirm = 0;
+-	Key **keys, *k;
++	char *provider = NULL, *pin;
++	int i, type, version, count = 0, success = 0, death = 0, confirm = 0;
++	Key **keys = NULL, *k;
+ 	Identity *id;
+ 	Idtab *tab;
+ 
+-	sc_reader_id = buffer_get_string(&e->request, NULL);
++	provider = buffer_get_string(&e->request, NULL);
+ 	pin = buffer_get_string(&e->request, NULL);
+ 
+ 	while (buffer_len(&e->request)) {
+@@ -638,30 +641,22 @@ process_add_smartcard_key(SocketEntry *e
+ 		default:
+ 			error("process_add_smartcard_key: "
+ 			    "Unknown constraint type %d", type);
+-			xfree(sc_reader_id);
+-			xfree(pin);
+ 			goto send;
+ 		}
+ 	}
+ 	if (lifetime && !death)
+ 		death = time(NULL) + lifetime;
+ 
+-	keys = sc_get_keys(sc_reader_id, pin);
+-	xfree(sc_reader_id);
+-	xfree(pin);
+-
+-	if (keys == NULL || keys[0] == NULL) {
+-		error("sc_get_keys failed");
+-		goto send;
+-	}
+-	for (i = 0; keys[i] != NULL; i++) {
++	count = pkcs11_add_provider(provider, pin, &keys);
++	for (i = 0; i < count; i++) {
+ 		k = keys[i];
+ 		version = k->type == KEY_RSA1 ? 1 : 2;
+ 		tab = idtab_lookup(version);
+ 		if (lookup_identity(k, version) == NULL) {
+-			id = xmalloc(sizeof(Identity));
++			id = xcalloc(1, sizeof(Identity));
+ 			id->key = k;
+-			id->comment = sc_get_key_label(k);
++			id->provider = xstrdup(provider);
++			id->comment = xstrdup(provider); /* XXX */
+ 			id->death = death;
+ 			id->confirm = confirm;
+ 			TAILQ_INSERT_TAIL(&tab->idlist, id, next);
+@@ -672,8 +667,13 @@ process_add_smartcard_key(SocketEntry *e
+ 		}
+ 		keys[i] = NULL;
+ 	}
+-	xfree(keys);
+ send:
++	if (pin)
++		xfree(pin);
++	if (provider)
++		xfree(provider);
++	if (keys)
++		xfree(keys);
+ 	buffer_put_int(&e->output, 1);
+ 	buffer_put_char(&e->output,
+ 	    success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
+@@ -682,42 +682,37 @@ send:
+ static void
+ process_remove_smartcard_key(SocketEntry *e)
+ {
+-	char *sc_reader_id = NULL, *pin;
+-	int i, version, success = 0;
+-	Key **keys, *k = NULL;
+-	Identity *id;
++	char *provider = NULL, *pin = NULL;
++	int version, success = 0;
++	Identity *id, *nxt;
+ 	Idtab *tab;
+ 
+-	sc_reader_id = buffer_get_string(&e->request, NULL);
++	provider = buffer_get_string(&e->request, NULL);
+ 	pin = buffer_get_string(&e->request, NULL);
+-	keys = sc_get_keys(sc_reader_id, pin);
+-	xfree(sc_reader_id);
+ 	xfree(pin);
+ 
+-	if (keys == NULL || keys[0] == NULL) {
+-		error("sc_get_keys failed");
+-		goto send;
+-	}
+-	for (i = 0; keys[i] != NULL; i++) {
+-		k = keys[i];
+-		version = k->type == KEY_RSA1 ? 1 : 2;
+-		if ((id = lookup_identity(k, version)) != NULL) {
+-			tab = idtab_lookup(version);
+-			TAILQ_REMOVE(&tab->idlist, id, next);
+-			tab->nentries--;
+-			free_identity(id);
+-			success = 1;
++	for (version = 1; version < 3; version++) {
++		tab = idtab_lookup(version);
++		for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) {
++			nxt = TAILQ_NEXT(id, next);
++			if (!strcmp(provider, id->provider)) {
++				TAILQ_REMOVE(&tab->idlist, id, next);
++				free_identity(id);
++				tab->nentries--;
++			}
+ 		}
+-		key_free(k);
+-		keys[i] = NULL;
+ 	}
+-	xfree(keys);
+-send:
++	if (pkcs11_del_provider(provider) == 0)
++		success = 1;
++	else
++		error("process_remove_smartcard_key:"
++		    " pkcs11_del_provider failed");
++	xfree(provider);
+ 	buffer_put_int(&e->output, 1);
+ 	buffer_put_char(&e->output,
+ 	    success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
+ }
+-#endif /* SMARTCARD */
++#endif /* ENABLE_PKCS11 */
+ 
+ #ifdef HAVE_LIBNSS
+ static void
+@@ -910,7 +905,7 @@ process_message(SocketEntry *e)
+ 	case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
+ 		process_remove_all_identities(e, 2);
+ 		break;
+-#ifdef SMARTCARD
++#ifdef ENABLE_PKCS11
+ 	case SSH_AGENTC_ADD_SMARTCARD_KEY:
+ 	case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED:
+ 		process_add_smartcard_key(e);
+@@ -927,7 +922,7 @@ process_message(SocketEntry *e)
+ 	case SSH_AGENTC_REMOVE_NSS_KEY:
+ 		process_remove_nss_key(e);
+ 		break;
+-#endif /* SMARTCARD */
++#endif /* ENABLE_PKCS11 */
+ 	default:
+ 		/* Unknown message.  Respond with failure. */
+ 		error("Unknown message %d", type);
+@@ -1131,6 +1126,9 @@ static void
+ cleanup_handler(int sig)
+ {
+ 	cleanup_socket();
++#ifdef ENABLE_PKCS11
++	pkcs11_terminate();
++#endif
+ 	_exit(2);
+ }
+ 
+@@ -1377,6 +1375,10 @@ main(int ac, char **av)
+ #endif
+ 
+ skip:
++
++#ifdef ENABLE_PKCS11
++	pkcs11_init(0);
++#endif
+ 	new_socket(AUTH_SOCKET, sock);
+ 	if (ac > 0)
+ 		parent_alive_interval = 10;
+diff -up openssh-5.3p1/ssh_config.5.pkcs11 openssh-5.3p1/ssh_config.5
+--- openssh-5.3p1/ssh_config.5.pkcs11	2013-07-10 14:34:46.262673757 +0200
++++ openssh-5.3p1/ssh_config.5	2013-07-10 14:34:46.509673758 +0200
+@@ -731,6 +731,13 @@ or
+ .Dq no .
+ The default is
+ .Dq no .
++.It Cm PKCS11Provider
++Specifies which PKCS#11 provider to use.
++The argument to this keyword is the PKCS#11 shared libary
++.Xr ssh 1
++should use to communicate with a PKCS#11 token used for storing the user's
++private RSA key.
++By default, no device is specified and PKCS#11 support is not activated.
+ .It Cm Port
+ Specifies the port number to connect on the remote host.
+ The default is 22.
+@@ -945,13 +952,6 @@ channel to request a response from the s
+ The default
+ is 0, indicating that these messages will not be sent to the server.
+ This option applies to protocol version 2 only.
+-.It Cm SmartcardDevice
+-Specifies which smartcard device to use.
+-The argument to this keyword is the device
+-.Xr ssh 1
+-should use to communicate with a smartcard used for storing the user's
+-private RSA key.
+-By default, no device is specified and smartcard support is not activated.
+ .It Cm StrictHostKeyChecking
+ If this flag is set to
+ .Dq yes ,
+diff -up openssh-5.3p1/ssh-keygen.1.pkcs11 openssh-5.3p1/ssh-keygen.1
+--- openssh-5.3p1/ssh-keygen.1.pkcs11	2013-07-10 14:34:46.413673758 +0200
++++ openssh-5.3p1/ssh-keygen.1	2013-07-10 14:34:46.513673757 +0200
+@@ -79,7 +79,7 @@
+ .Fl B
+ .Op Fl f Ar input_keyfile
+ .Nm ssh-keygen
+-.Fl D Ar reader
++.Fl D Ar pkcs11
+ .Nm ssh-keygen
+ .Fl F Ar hostname
+ .Op Fl f Ar known_hosts_file
+@@ -91,9 +91,6 @@
+ .Fl R Ar hostname
+ .Op Fl f Ar known_hosts_file
+ .Nm ssh-keygen
+-.Fl U Ar reader
+-.Op Fl f Ar input_keyfile
+-.Nm ssh-keygen
+ .Fl r Ar hostname
+ .Op Fl f Ar input_keyfile
+ .Op Fl g
+@@ -204,9 +201,10 @@ Requests changing the comment in the pri
+ This operation is only supported for RSA1 keys.
+ The program will prompt for the file containing the private keys, for
+ the passphrase if the key has one, and for the new comment.
+-.It Fl D Ar reader
+-Download the RSA public key stored in the smartcard in
+-.Ar reader .
++.It Fl D Ar pkcs11
++Download the RSA public keys stored in the
++.Ar pkcs11
++provider.
+ .It Fl e
+ This option will read a private or public OpenSSH key file and
+ print the key in
+@@ -318,9 +316,6 @@ for protocol version 1 and
+ or
+ .Dq dsa
+ for protocol version 2.
+-.It Fl U Ar reader
+-Upload an existing RSA private key into the smartcard in
+-.Ar reader .
+ .It Fl v
+ Verbose mode.
+ Causes
+diff -up openssh-5.3p1/ssh-keygen.c.pkcs11 openssh-5.3p1/ssh-keygen.c
+--- openssh-5.3p1/ssh-keygen.c.pkcs11	2013-07-10 14:34:46.219673758 +0200
++++ openssh-5.3p1/ssh-keygen.c	2013-07-10 14:34:46.513673757 +0200
+@@ -50,8 +50,8 @@
+ #include "hostfile.h"
+ #include "dns.h"
+ 
+-#ifdef SMARTCARD
+-#include "scard.h"
++#ifdef ENABLE_PKCS11
++#include "ssh-pkcs11.h"
+ #endif
+ 
+ #ifdef HAVE_LIBNSS
+@@ -461,51 +461,29 @@ do_print_public(struct passwd *pw)
+ 	exit(0);
+ }
+ 
+-#ifdef SMARTCARD
+-static void
+-do_upload(struct passwd *pw, const char *sc_reader_id)
+-{
+-	Key *prv = NULL;
+-	struct stat st;
+-	int ret;
+-
+-	if (!have_identity)
+-		ask_filename(pw, "Enter file in which the key is");
+-	if (stat(identity_file, &st) < 0) {
+-		perror(identity_file);
+-		exit(1);
+-	}
+-	prv = load_identity(identity_file);
+-	if (prv == NULL) {
+-		error("load failed");
+-		exit(1);
+-	}
+-	ret = sc_put_key(prv, sc_reader_id);
+-	key_free(prv);
+-	if (ret < 0)
+-		exit(1);
+-	logit("loading key done");
+-	exit(0);
+-}
+-
+ static void
+-do_download(struct passwd *pw, const char *sc_reader_id)
++do_download(struct passwd *pw, char *pkcs11provider)
+ {
++#ifdef ENABLE_PKCS11
+ 	Key **keys = NULL;
+-	int i;
++	int i, nkeys;
+ 
+-	keys = sc_get_keys(sc_reader_id, NULL);
+-	if (keys == NULL)
+-		fatal("cannot read public key from smartcard");
+-	for (i = 0; keys[i]; i++) {
++	pkcs11_init(0);
++	nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys);
++	if (nkeys <= 0)
++		fatal("cannot read public key from pkcs11");
++	for (i = 0; i < nkeys; i++) {
+ 		key_write(keys[i], stdout);
+ 		key_free(keys[i]);
+ 		fprintf(stdout, "\n");
+ 	}
+ 	xfree(keys);
++	pkcs11_terminate();
+ 	exit(0);
++#else
++	fatal("no pkcs11 support");
++#endif /* ENABLE_PKCS11 */
+ }
+-#endif /* SMARTCARD */
+ 
+ #ifdef HAVE_LIBNSS
+ static void
+@@ -1066,9 +1044,9 @@ usage(void)
+ 	fprintf(stderr, "  -b bits     Number of bits in the key to create.\n");
+ 	fprintf(stderr, "  -C comment  Provide new comment.\n");
+ 	fprintf(stderr, "  -c          Change comment in private and public key files.\n");
+-#ifdef SMARTCARD
+-	fprintf(stderr, "  -D reader   Download public key from smartcard.\n");
+-#endif /* SMARTCARD */
++#ifdef ENABLE_PKCS11
++	fprintf(stderr, "  -D pkcs11   Download public key from pkcs11 token.\n");
++#endif
+ 	fprintf(stderr, "  -e          Convert OpenSSH to RFC 4716 key file.\n");
+ 	fprintf(stderr, "  -F hostname Find hostname in known hosts file.\n");
+ 	fprintf(stderr, "  -f filename Filename of the key file.\n");
+@@ -1090,9 +1068,6 @@ usage(void)
+ 	fprintf(stderr, "  -S start    Start point (hex) for generating DH-GEX moduli.\n");
+ 	fprintf(stderr, "  -T file     Screen candidates for DH-GEX moduli.\n");
+ 	fprintf(stderr, "  -t type     Specify type of key to create.\n");
+-#ifdef SMARTCARD
+-	fprintf(stderr, "  -U reader   Upload private key to smartcard.\n");
+-#endif /* SMARTCARD */
+ 	fprintf(stderr, "  -v          Verbose.\n");
+ 	fprintf(stderr, "  -W gen      Generator to use for generating DH-GEX moduli.\n");
+ 	fprintf(stderr, "  -y          Read private key file and print public key.\n");
+@@ -1107,7 +1082,7 @@ int
+ main(int argc, char **argv)
+ {
+ 	char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
+-	char out_file[MAXPATHLEN], *reader_id = NULL;
++	char out_file[MAXPATHLEN], *pkcs11provider = NULL;
+ 	char *rr_hostname = NULL;
+ 	Key *private, *public;
+ 	struct passwd *pw;
+@@ -1146,7 +1121,7 @@ main(int argc, char **argv)
+ 	}
+ 
+ 	while ((opt = getopt(argc, argv,
+-	    "degiqpclnBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) {
++	    "degiqpclnBHvxXyF:b:f:t:D:N:P:C:r:g:R:T:G:M:S:a:W:")) != -1) {
+ 		switch (opt) {
+ 		case 'b':
+ 			bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr);
+@@ -1221,11 +1196,8 @@ main(int argc, char **argv)
+ 		case 't':
+ 			key_type_name = optarg;
+ 			break;
+-		case 'U':
+-			download = 0;
+-			/*FALLTHROUGH*/
+ 		case 'D':
+-			reader_id = optarg;
++			pkcs11provider = optarg;
+ 			break;
+ 		case 'v':
+ 			if (log_level == SYSLOG_LEVEL_INFO)
+@@ -1337,23 +1309,15 @@ main(int argc, char **argv)
+ 	if (use_nss) {
+ #ifdef HAVE_LIBNSS
+ 		if (download)
+-			do_nss_download(pw, reader_id, identity_file);
++			do_nss_download(pw, pkcs11provider, identity_file);
+ 		else
+ 			fatal("no support for NSS key upload.");
+ #else
+ 		fatal("no support for NSS keys.");
+ #endif
+ 	}
+-	if (reader_id != NULL) {
+-#ifdef SMARTCARD
+-		if (download)
+-			do_download(pw, reader_id);
+-		else
+-			do_upload(pw, reader_id);
+-#else /* SMARTCARD */
+-		fatal("no support for smartcards.");
+-#endif /* SMARTCARD */
+-	}
++	if (pkcs11provider != NULL)
++		do_download(pw, pkcs11provider);
+ 
+ 	if (do_gen_candidates) {
+ 		FILE *out = fopen(out_file, "w");
+diff -up openssh-5.3p1/ssh-pkcs11-client.c.pkcs11 openssh-5.3p1/ssh-pkcs11-client.c
+--- openssh-5.3p1/ssh-pkcs11-client.c.pkcs11	2013-07-10 14:34:46.514673757 +0200
++++ openssh-5.3p1/ssh-pkcs11-client.c	2013-07-10 14:34:46.514673757 +0200
+@@ -0,0 +1,229 @@
++/*
++ * Copyright (c) 2010 Markus Friedl.  All rights reserved.
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <sys/types.h>
++#include <sys/time.h>
++#include <sys/socket.h>
++
++#include <stdarg.h>
++#include <string.h>
++#include <unistd.h>
++#include <errno.h>
++
++#include "pathnames.h"
++#include "xmalloc.h"
++#include "buffer.h"
++#include "log.h"
++#include "misc.h"
++#include "key.h"
++#include "authfd.h"
++#include "atomicio.h"
++#include "ssh-pkcs11.h"
++
++/* borrows code from sftp-server and ssh-agent */
++
++int fd = -1;
++pid_t pid = -1;
++
++static void
++send_msg(Buffer *m)
++{
++	u_char buf[4];
++	int mlen = buffer_len(m);
++
++	put_u32(buf, mlen);
++	if (atomicio(vwrite, fd, buf, 4) != 4 ||
++	    atomicio(vwrite, fd, buffer_ptr(m),
++	    buffer_len(m)) != buffer_len(m))
++		error("write to helper failed");
++	buffer_consume(m, mlen);
++}
++
++static int
++recv_msg(Buffer *m)
++{
++	u_int l, len;
++	u_char buf[1024];
++
++	if ((len = atomicio(read, fd, buf, 4)) != 4) {
++		error("read from helper failed: %u", len);
++		return (0); /* XXX */
++	}
++	len = get_u32(buf);
++	if (len > 256 * 1024)
++		fatal("response too long: %u", len);
++	/* read len bytes into m */
++	buffer_clear(m);
++	while (len > 0) {
++		l = len;
++		if (l > sizeof(buf))
++			l = sizeof(buf);
++		if (atomicio(read, fd, buf, l) != l) {
++			error("response from helper failed.");
++			return (0); /* XXX */
++		}
++		buffer_append(m, buf, l);
++		len -= l;
++	}
++	return (buffer_get_char(m));
++}
++
++int
++pkcs11_init(int interactive)
++{
++	return (0);
++}
++
++void
++pkcs11_terminate(void)
++{
++	close(fd);
++}
++
++static int
++pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
++    int padding)
++{
++	Key key;
++	u_char *blob, *signature = NULL;
++	u_int blen, slen = 0;
++	int ret = -1;
++	Buffer msg;
++
++	if (padding != RSA_PKCS1_PADDING)
++		return (-1);
++	key.type = KEY_RSA;
++	key.rsa = rsa;
++	if (key_to_blob(&key, &blob, &blen) == 0)
++		return -1;
++	buffer_init(&msg);
++	buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
++	buffer_put_string(&msg, blob, blen);
++	buffer_put_string(&msg, from, flen);
++	buffer_put_int(&msg, 0);
++	xfree(blob);
++	send_msg(&msg);
++
++	if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) {
++		signature = buffer_get_string(&msg, &slen);
++		if (slen <= (u_int)RSA_size(rsa)) {
++			memcpy(to, signature, slen);
++			ret = slen;
++		}
++		xfree(signature);
++	}
++	return (ret);
++}
++
++/* redirect the private key encrypt operation to the ssh-pkcs11-helper */
++static int
++wrap_key(RSA *rsa)
++{
++	static RSA_METHOD helper_rsa;
++
++	memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa));
++	helper_rsa.name = "ssh-pkcs11-helper";
++	helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt;
++	RSA_set_method(rsa, &helper_rsa);
++	return (0);
++}
++
++static int
++pkcs11_start_helper(void)
++{
++	int pair[2];
++
++	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
++		error("socketpair: %s", strerror(errno));
++		return (-1);
++	}
++	if ((pid = fork()) == -1) {
++		error("fork: %s", strerror(errno));
++		return (-1);
++	} else if (pid == 0) {
++		if ((dup2(pair[1], STDIN_FILENO) == -1) ||
++		    (dup2(pair[1], STDOUT_FILENO) == -1)) {
++			fprintf(stderr, "dup2: %s\n", strerror(errno));
++			_exit(1);
++		}
++		close(pair[0]);
++		close(pair[1]);
++		execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER,
++		    (char *) 0);
++		fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER,
++		    strerror(errno));
++		_exit(1);
++	}
++	close(pair[1]);
++	fd = pair[0];
++	return (0);
++}
++
++int
++pkcs11_add_provider(char *name, char *pin, Key ***keysp)
++{
++	Key *k;
++	int i, nkeys;
++	u_char *blob;
++	u_int blen;
++	Buffer msg;
++
++	if (fd < 0 && pkcs11_start_helper() < 0)
++		return (-1);
++
++	buffer_init(&msg);
++	buffer_put_char(&msg, SSH_AGENTC_ADD_SMARTCARD_KEY);
++	buffer_put_cstring(&msg, name);
++	buffer_put_cstring(&msg, pin);
++	send_msg(&msg);
++	buffer_clear(&msg);
++
++	if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) {
++		nkeys = buffer_get_int(&msg);
++		*keysp = xcalloc(nkeys, sizeof(Key *));
++		for (i = 0; i < nkeys; i++) {
++			blob = buffer_get_string(&msg, &blen);
++			xfree(buffer_get_string(&msg, NULL));
++			k = key_from_blob(blob, blen);
++			wrap_key(k->rsa);
++			(*keysp)[i] = k;
++			xfree(blob);
++		}
++	} else {
++		nkeys = -1;
++	}
++	buffer_free(&msg);
++	return (nkeys);
++}
++
++int
++pkcs11_del_provider(char *name)
++{
++	int ret = -1;
++	Buffer msg;
++
++	buffer_init(&msg);
++	buffer_put_char(&msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY);
++	buffer_put_cstring(&msg, name);
++	buffer_put_cstring(&msg, "");
++	send_msg(&msg);
++	buffer_clear(&msg);
++
++	if (recv_msg(&msg) == SSH_AGENT_SUCCESS)
++		ret = 0;
++	buffer_free(&msg);
++	return (ret);
++}
+diff -up openssh-5.3p1/ssh-pkcs11.c.pkcs11 openssh-5.3p1/ssh-pkcs11.c
+--- openssh-5.3p1/ssh-pkcs11.c.pkcs11	2013-07-10 14:34:46.516673757 +0200
++++ openssh-5.3p1/ssh-pkcs11.c	2013-07-10 14:34:46.516673757 +0200
+@@ -0,0 +1,544 @@
++/*
++ * Copyright (c) 2010 Markus Friedl.  All rights reserved.
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <sys/types.h>
++#include <sys/queue.h>
++#include <stdarg.h>
++#include <stdio.h>
++
++#include <string.h>
++#include <dlfcn.h>
++
++#define CRYPTOKI_COMPAT
++#include "pkcs11.h"
++
++#include "log.h"
++#include "misc.h"
++#include "key.h"
++#include "ssh-pkcs11.h"
++#include "xmalloc.h"
++
++struct pkcs11_slotinfo {
++	CK_TOKEN_INFO		token;
++	CK_SESSION_HANDLE	session;
++	int			logged_in;
++};
++
++struct pkcs11_provider {
++	char			*name;
++	void			*handle;
++	CK_FUNCTION_LIST	*function_list;
++	CK_INFO			info;
++	CK_ULONG		nslots;
++	CK_SLOT_ID		*slotlist;
++	struct pkcs11_slotinfo	*slotinfo;
++	int			valid;
++	int			refcount;
++	TAILQ_ENTRY(pkcs11_provider) next;
++};
++
++TAILQ_HEAD(, pkcs11_provider) pkcs11_providers;
++
++struct pkcs11_key {
++	struct pkcs11_provider	*provider;
++	CK_ULONG		slotidx;
++	int			(*orig_finish)(RSA *rsa);
++	RSA_METHOD		rsa_method;
++	char			*keyid;
++	int			keyid_len;
++};
++
++int pkcs11_interactive = 0;
++
++int
++pkcs11_init(int interactive)
++{
++	pkcs11_interactive = interactive;
++	TAILQ_INIT(&pkcs11_providers);
++	return (0);
++}
++
++/*
++ * finalize a provider shared libarary, it's no longer usable.
++ * however, there might still be keys referencing this provider,
++ * so the actuall freeing of memory is handled by pkcs11_provider_unref().
++ * this is called when a provider gets unregistered.
++ */
++static void
++pkcs11_provider_finalize(struct pkcs11_provider *p)
++{
++	CK_RV rv;
++	CK_ULONG i;
++
++	debug("pkcs11_provider_finalize: %p refcount %d valid %d",
++	    p, p->refcount, p->valid);
++	if (!p->valid)
++		return;
++	for (i = 0; i < p->nslots; i++) {
++		if (p->slotinfo[i].session &&
++		    (rv = p->function_list->C_CloseSession(
++		    p->slotinfo[i].session)) != CKR_OK)
++			error("C_CloseSession failed: %lu", rv);
++	}
++	if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
++		error("C_Finalize failed: %lu", rv);
++	p->valid = 0;
++	p->function_list = NULL;
++	dlclose(p->handle);
++}
++
++/*
++ * remove a reference to the provider.
++ * called when a key gets destroyed or when the provider is unregistered.
++ */
++static void
++pkcs11_provider_unref(struct pkcs11_provider *p)
++{
++	debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
++	if (--p->refcount <= 0) {
++		if (p->valid)
++			error("pkcs11_provider_unref: %p still valid", p);
++		xfree(p->slotlist);
++		xfree(p->slotinfo);
++		xfree(p);
++	}
++}
++
++/* unregister all providers, keys might still point to the providers */
++void
++pkcs11_terminate(void)
++{
++	struct pkcs11_provider *p;
++
++	while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
++		TAILQ_REMOVE(&pkcs11_providers, p, next);
++		pkcs11_provider_finalize(p);
++		pkcs11_provider_unref(p);
++	}
++}
++
++/* lookup provider by name */
++static struct pkcs11_provider *
++pkcs11_provider_lookup(char *provider_id)
++{
++	struct pkcs11_provider *p;
++
++	TAILQ_FOREACH(p, &pkcs11_providers, next) {
++		debug("check %p %s", p, p->name);
++		if (!strcmp(provider_id, p->name))
++			return (p);
++	}
++	return (NULL);
++}
++
++/* unregister provider by name */
++int
++pkcs11_del_provider(char *provider_id)
++{
++	struct pkcs11_provider *p;
++
++	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
++		TAILQ_REMOVE(&pkcs11_providers, p, next);
++		pkcs11_provider_finalize(p);
++		pkcs11_provider_unref(p);
++		return (0);
++	}
++	return (-1);
++}
++
++/* openssl callback for freeing an RSA key */
++static int
++pkcs11_rsa_finish(RSA *rsa)
++{
++	struct pkcs11_key	*k11;
++	int rv = -1;
++
++	if ((k11 = RSA_get_app_data(rsa)) != NULL) {
++		if (k11->orig_finish)
++			rv = k11->orig_finish(rsa);
++		if (k11->provider)
++			pkcs11_provider_unref(k11->provider);
++		if (k11->keyid)
++			xfree(k11->keyid);
++		xfree(k11);
++	}
++	return (rv);
++}
++
++/* openssl callback doing the actual signing operation */
++static int
++pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
++    int padding)
++{
++	struct pkcs11_key	*k11;
++	struct pkcs11_slotinfo	*si;
++	CK_FUNCTION_LIST	*f;
++	CK_OBJECT_HANDLE	obj;
++	CK_ULONG		tlen = 0, nfound = 0;
++	CK_RV			rv;
++	CK_OBJECT_CLASS		private_key_class = CKO_PRIVATE_KEY;
++	CK_BBOOL		true = CK_TRUE;
++	CK_MECHANISM		mech = {
++		CKM_RSA_PKCS, NULL_PTR, 0
++	};
++	CK_ATTRIBUTE		key_filter[] = {
++		{CKA_CLASS, &private_key_class, sizeof(private_key_class) },
++		{CKA_ID, NULL, 0},
++		{CKA_SIGN, &true, sizeof(true) }
++	};
++	char			*pin, prompt[1024];
++	int			rval = -1;
++
++	if ((k11 = RSA_get_app_data(rsa)) == NULL) {
++		error("RSA_get_app_data failed for rsa %p", rsa);
++		return (-1);
++	}
++	if (!k11->provider || !k11->provider->valid) {
++		error("no pkcs11 (valid) provider for rsa %p", rsa);
++		return (-1);
++	}
++	f = k11->provider->function_list;
++	si = &k11->provider->slotinfo[k11->slotidx];
++	if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
++		if (!pkcs11_interactive) {
++			error("need pin");
++			return (-1);
++		}
++		snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
++		    si->token.label);
++		pin = read_passphrase(prompt, RP_ALLOW_EOF);
++		if (pin == NULL)
++			return (-1);	/* bail out */
++		if ((rv = f->C_Login(si->session, CKU_USER, pin, strlen(pin)))
++		    != CKR_OK) {
++			xfree(pin);
++			error("C_Login failed: %lu", rv);
++			return (-1);
++		}
++		xfree(pin);
++		si->logged_in = 1;
++	}
++	key_filter[1].pValue = k11->keyid;
++	key_filter[1].ulValueLen = k11->keyid_len;
++	if ((rv = f->C_FindObjectsInit(si->session, key_filter, 3)) != CKR_OK) {
++		error("C_FindObjectsInit failed: %lu", rv);
++		return (-1);
++	}
++	if ((rv = f->C_FindObjects(si->session, &obj, 1, &nfound)) != CKR_OK ||
++	    nfound != 1) {
++		error("C_FindObjects failed (%lu nfound): %lu", nfound, rv);
++	} else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
++		error("C_SignInit failed: %lu", rv);
++	} else {
++		/* XXX handle CKR_BUFFER_TOO_SMALL */
++		tlen = RSA_size(rsa);
++		rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
++		if (rv == CKR_OK) 
++			rval = tlen;
++		else 
++			error("C_Sign failed: %lu", rv);
++	}
++	if ((rv = f->C_FindObjectsFinal(si->session)) != CKR_OK)
++		error("C_FindObjectsFinal failed: %lu", rv);
++	return (rval);
++}
++
++static int
++pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
++    int padding)
++{
++	return (-1);
++}
++
++/* redirect private key operations for rsa key to pkcs11 token */
++static int
++pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
++    CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
++{
++	struct pkcs11_key	*k11;
++	const RSA_METHOD	*def = RSA_get_default_method();
++
++	k11 = xcalloc(1, sizeof(*k11));
++	k11->provider = provider;
++	provider->refcount++;	/* provider referenced by RSA key */
++	k11->slotidx = slotidx;
++	/* identify key object on smartcard */
++	k11->keyid_len = keyid_attrib->ulValueLen;
++	k11->keyid = xmalloc(k11->keyid_len);
++	memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
++	k11->orig_finish = def->finish;
++	memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method));
++	k11->rsa_method.name = "pkcs11";
++	k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt;
++	k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt;
++	k11->rsa_method.finish = pkcs11_rsa_finish;
++	RSA_set_method(rsa, &k11->rsa_method);
++	RSA_set_app_data(rsa, k11);
++	return (0);
++}
++
++/* remove trailing spaces */
++static void
++rmspace(char *buf, size_t len)
++{
++	size_t i;
++
++	if (!len)
++		return;
++	for (i = len - 1;  i > 0; i--)
++		if (i == len - 1 || buf[i] == ' ')
++			buf[i] = '\0';
++		else
++			break;
++}
++
++/*
++ * open a pkcs11 session and login if required.
++ * if pin == NULL we delay login until key use
++ */
++static int
++pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin)
++{
++	CK_RV			rv;
++	CK_FUNCTION_LIST	*f;
++	CK_SESSION_HANDLE	session;
++	int			login_required;
++
++	f = p->function_list;
++	login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED;
++	if (pin && login_required && !strlen(pin)) {
++		error("pin required");
++		return (-1);
++	}
++	if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
++	    CKF_SERIAL_SESSION, NULL, NULL, &session))
++	    != CKR_OK) {
++		error("C_OpenSession failed: %lu", rv);
++		return (-1);
++	}
++	if (login_required && pin) {
++		if ((rv = f->C_Login(session, CKU_USER, pin, strlen(pin)))
++		    != CKR_OK) {
++			error("C_Login failed: %lu", rv);
++			if ((rv = f->C_CloseSession(session)) != CKR_OK)
++				error("C_CloseSession failed: %lu", rv);
++			return (-1);
++		}
++		p->slotinfo[slotidx].logged_in = 1;
++	}
++	p->slotinfo[slotidx].session = session;
++	return (0);
++}
++
++/*
++ * lookup public keys for token in slot identified by slotidx,
++ * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
++ * keysp points to an (possibly empty) array with *nkeys keys.
++ */
++static int
++pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp,
++    int *nkeys)
++{
++	Key			*key;
++	RSA			*rsa;
++	int			i;
++	CK_RV			rv;
++	CK_OBJECT_HANDLE	obj;
++	CK_ULONG		nfound;
++	CK_SESSION_HANDLE	session;
++	CK_FUNCTION_LIST	*f;
++	CK_OBJECT_CLASS		pubkey_class = CKO_PUBLIC_KEY;
++	CK_ATTRIBUTE		pubkey_filter[] = {
++		{ CKA_CLASS, &pubkey_class, sizeof(pubkey_class) }
++	};
++	CK_ATTRIBUTE		attribs[] = {
++		{ CKA_ID, NULL, 0 },
++		{ CKA_MODULUS, NULL, 0 },
++		{ CKA_PUBLIC_EXPONENT, NULL, 0 }
++	};
++
++	f = p->function_list;
++	session = p->slotinfo[slotidx].session;
++	/* setup a filter the looks for public keys */
++	if ((rv = f->C_FindObjectsInit(session, pubkey_filter, 1)) != CKR_OK) {
++		error("C_FindObjectsInit failed: %lu", rv);
++		return (-1);
++	}
++	while (1) {
++		/* XXX 3 attributes in attribs[] */
++		for (i = 0; i < 3; i++) {
++			attribs[i].pValue = NULL;
++			attribs[i].ulValueLen = 0;
++		}
++		if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK
++		    || nfound == 0)
++			break;
++		/* found a key, so figure out size of the attributes */
++		if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3))
++		    != CKR_OK) {
++			error("C_GetAttributeValue failed: %lu", rv);
++			continue;
++		}
++		/* allocate buffers for attributes, XXX check ulValueLen? */
++		for (i = 0; i < 3; i++)
++			attribs[i].pValue = xmalloc(attribs[i].ulValueLen);
++		/* retrieve ID, modulus and public exponent of RSA key */
++		if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3))
++		    != CKR_OK) {
++			error("C_GetAttributeValue failed: %lu", rv);
++		} else if ((rsa = RSA_new()) == NULL) {
++			error("RSA_new failed");
++		} else {
++			rsa->n = BN_bin2bn(attribs[1].pValue,
++			    attribs[1].ulValueLen, NULL);
++			rsa->e = BN_bin2bn(attribs[2].pValue,
++			    attribs[2].ulValueLen, NULL);
++			if (rsa->n && rsa->e &&
++			    pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) {
++				key = key_new(KEY_UNSPEC);
++				key->rsa = rsa;
++				key->type = KEY_RSA;
++				key->flags |= KEY_FLAG_EXT;
++				/* expand key array and add key */
++				*keysp = xrealloc(*keysp, *nkeys + 1,
++				    sizeof(Key *));
++				(*keysp)[*nkeys] = key;
++				*nkeys = *nkeys + 1;
++				debug("have %d keys", *nkeys);
++			} else {
++				RSA_free(rsa);
++			}
++		}
++		for (i = 0; i < 3; i++)
++			xfree(attribs[i].pValue);
++	}
++	if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
++		error("C_FindObjectsFinal failed: %lu", rv);
++	return (0);
++}
++
++/* register a new provider, fails if provider already exists */
++int
++pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp)
++{
++	int nkeys, need_finalize = 0;
++	struct pkcs11_provider *p = NULL;
++	void *handle = NULL;
++	CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
++	CK_RV rv;
++	CK_FUNCTION_LIST *f = NULL;
++	CK_TOKEN_INFO *token;
++	CK_ULONG i;
++
++	*keyp = NULL;
++	if (pkcs11_provider_lookup(provider_id) != NULL) {
++		error("provider already registered: %s", provider_id);
++		goto fail;
++	}
++	/* open shared pkcs11-libarary */
++	if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
++		error("dlopen %s failed: %s", provider_id, dlerror());
++		goto fail;
++	}
++	if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
++		error("dlsym(C_GetFunctionList) failed: %s", dlerror());
++		goto fail;
++	}
++	p = xcalloc(1, sizeof(*p));
++	p->name = xstrdup(provider_id);
++	p->handle = handle;
++	/* setup the pkcs11 callbacks */
++	if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
++		error("C_GetFunctionList failed: %lu", rv);
++		goto fail;
++	}
++	p->function_list = f;
++	if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
++		error("C_Initialize failed: %lu", rv);
++		goto fail;
++	}
++	need_finalize = 1;
++	if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
++		error("C_GetInfo failed: %lu", rv);
++		goto fail;
++	}
++	rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
++	rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
++	debug("manufacturerID <%s> cryptokiVersion %d.%d"
++	    " libraryDescription <%s> libraryVersion %d.%d",
++	    p->info.manufacturerID,
++	    p->info.cryptokiVersion.major,
++	    p->info.cryptokiVersion.minor,
++	    p->info.libraryDescription,
++	    p->info.libraryVersion.major,
++	    p->info.libraryVersion.minor);
++	if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
++		error("C_GetSlotList failed: %lu", rv);
++		goto fail;
++	}
++	if (p->nslots == 0) {
++		error("no slots");
++		goto fail;
++	}
++	p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
++	if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
++	    != CKR_OK) {
++		error("C_GetSlotList failed: %lu", rv);
++		goto fail;
++	}
++	p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
++	p->valid = 1;
++	nkeys = 0;
++	for (i = 0; i < p->nslots; i++) {
++		token = &p->slotinfo[i].token;
++		if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
++		    != CKR_OK) {
++			error("C_GetTokenInfo failed: %lu", rv);
++			continue;
++		}
++		rmspace(token->label, sizeof(token->label));
++		rmspace(token->manufacturerID, sizeof(token->manufacturerID));
++		rmspace(token->model, sizeof(token->model));
++		rmspace(token->serialNumber, sizeof(token->serialNumber));
++		debug("label <%s> manufacturerID <%s> model <%s> serial <%s>"
++		    " flags 0x%lx",
++		    token->label, token->manufacturerID, token->model,
++		    token->serialNumber, token->flags);
++		/* open session, login with pin and retrieve public keys */
++		if (pkcs11_open_session(p, i, pin) == 0)
++			pkcs11_fetch_keys(p, i, keyp, &nkeys);
++	}
++	if (nkeys > 0) {
++		TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
++		p->refcount++;	/* add to provider list */
++		return (nkeys);
++	}
++	error("no keys");
++	/* don't add the provider, since it does not have any keys */
++fail:
++	if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
++		error("C_Finalize failed: %lu", rv);
++	if (p) {
++		if (p->slotlist)
++			xfree(p->slotlist);
++		if (p->slotinfo)
++			xfree(p->slotinfo);
++		xfree(p);
++	}
++	if (handle)
++		dlclose(handle);
++	return (-1);
++}
+diff -up openssh-5.3p1/ssh-pkcs11-helper.c.pkcs11 openssh-5.3p1/ssh-pkcs11-helper.c
+--- openssh-5.3p1/ssh-pkcs11-helper.c.pkcs11	2013-07-10 14:34:46.517673757 +0200
++++ openssh-5.3p1/ssh-pkcs11-helper.c	2013-07-10 14:34:46.517673757 +0200
+@@ -0,0 +1,350 @@
++/*
++ * Copyright (c) 2010 Markus Friedl.  All rights reserved.
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <sys/queue.h>
++#include <sys/types.h>
++#include <sys/time.h>
++
++#include <stdarg.h>
++#include <string.h>
++#include <unistd.h>
++#include <errno.h>
++
++#include "xmalloc.h"
++#include "buffer.h"
++#include "includes.h"
++#include "log.h"
++#include "misc.h"
++#include "key.h"
++#include "authfd.h"
++#include "ssh-pkcs11.h"
++
++/* borrows code from sftp-server and ssh-agent */
++
++struct pkcs11_keyinfo {
++	Key		*key;
++	char		*providername;
++	TAILQ_ENTRY(pkcs11_keyinfo) next;
++};
++
++TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist;
++
++#define MAX_MSG_LENGTH		10240 /*XXX*/
++
++/* helper */
++#define get_int()			buffer_get_int(&iqueue);
++#define get_string(lenp)		buffer_get_string(&iqueue, lenp);
++
++/* input and output queue */
++Buffer iqueue;
++Buffer oqueue;
++
++static void
++add_key(Key *k, char *name)
++{
++	struct pkcs11_keyinfo *ki;
++
++	ki = xcalloc(1, sizeof(*ki));
++	ki->providername = xstrdup(name);
++	ki->key = k;
++	TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next);
++}
++
++static void
++del_keys_by_name(char *name)
++{
++	struct pkcs11_keyinfo *ki, *nxt;
++
++	for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) {
++		nxt = TAILQ_NEXT(ki, next);
++		if (!strcmp(ki->providername, name)) {
++			TAILQ_REMOVE(&pkcs11_keylist, ki, next);
++			xfree(ki->providername);
++			key_free(ki->key);
++			free(ki);
++		}
++	}
++}
++
++/* lookup matching 'private' key */
++static Key *
++lookup_key(Key *k)
++{
++	struct pkcs11_keyinfo *ki;
++
++	TAILQ_FOREACH(ki, &pkcs11_keylist, next) {
++		debug("check %p %s", ki, ki->providername);
++		if (key_equal(k, ki->key))
++			return (ki->key);
++	}
++	return (NULL);
++}
++
++static void
++send_msg(Buffer *m)
++{
++	int mlen = buffer_len(m);
++
++	buffer_put_int(&oqueue, mlen);
++	buffer_append(&oqueue, buffer_ptr(m), mlen);
++	buffer_consume(m, mlen);
++}
++
++static void
++process_add(void)
++{
++	char *name, *pin;
++	Key **keys;
++	int i, nkeys;
++	u_char *blob;
++	u_int blen;
++	Buffer msg;
++
++	buffer_init(&msg);
++	name = get_string(NULL);
++	pin = get_string(NULL);
++	if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) {
++		buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER);
++		buffer_put_int(&msg, nkeys);
++		for (i = 0; i < nkeys; i++) {
++			key_to_blob(keys[i], &blob, &blen);
++			buffer_put_string(&msg, blob, blen);
++			buffer_put_cstring(&msg, name);
++			xfree(blob);
++			add_key(keys[i], name);
++		}
++		xfree(keys);
++	} else {
++		buffer_put_char(&msg, SSH_AGENT_FAILURE);
++	}
++	xfree(pin);
++	xfree(name);
++	send_msg(&msg);
++	buffer_free(&msg);
++}
++
++static void
++process_del(void)
++{
++	char *name, *pin;
++	Buffer msg;
++
++	buffer_init(&msg);
++	name = get_string(NULL);
++	pin = get_string(NULL);
++	del_keys_by_name(name);
++	if (pkcs11_del_provider(name) == 0)
++		 buffer_put_char(&msg, SSH_AGENT_SUCCESS);
++	else
++		 buffer_put_char(&msg, SSH_AGENT_FAILURE);
++	xfree(pin);
++	xfree(name);
++	send_msg(&msg);
++	buffer_free(&msg);
++}
++
++static void
++process_sign(void)
++{
++	u_char *blob, *data, *signature = NULL;
++	u_int blen, dlen, slen = 0;
++	int ok = -1, flags, ret;
++	Key *key, *found;
++	Buffer msg;
++
++	blob = get_string(&blen);
++	data = get_string(&dlen);
++	flags = get_int(); /* XXX ignore */
++
++	if ((key = key_from_blob(blob, blen)) != NULL) {
++		if ((found = lookup_key(key)) != NULL) {
++			slen = RSA_size(key->rsa);
++			signature = xmalloc(slen);
++			if ((ret = RSA_private_encrypt(dlen, data, signature,
++			    found->rsa, RSA_PKCS1_PADDING)) != -1) {
++				slen = ret;
++				ok = 0;
++			}
++		}
++		key_free(key);
++	}
++	buffer_init(&msg);
++	if (ok == 0) {
++		buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE);
++		buffer_put_string(&msg, signature, slen);
++	} else {
++		buffer_put_char(&msg, SSH_AGENT_FAILURE);
++	}
++	xfree(data);
++	xfree(blob);
++	if (signature != NULL)
++		xfree(signature);
++	send_msg(&msg);
++	buffer_free(&msg);
++}
++
++static void
++process(void)
++{
++	u_int msg_len;
++	u_int buf_len;
++	u_int consumed;
++	u_int type;
++	u_char *cp;
++
++	buf_len = buffer_len(&iqueue);
++	if (buf_len < 5)
++		return;		/* Incomplete message. */
++	cp = buffer_ptr(&iqueue);
++	msg_len = get_u32(cp);
++	if (msg_len > MAX_MSG_LENGTH) {
++		error("bad message len %d", msg_len);
++		cleanup_exit(11);
++	}
++	if (buf_len < msg_len + 4)
++		return;
++	buffer_consume(&iqueue, 4);
++	buf_len -= 4;
++	type = buffer_get_char(&iqueue);
++	switch (type) {
++	case SSH_AGENTC_ADD_SMARTCARD_KEY:
++		debug("process_add");
++		process_add();
++		break;
++	case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
++		debug("process_del");
++		process_del();
++		break;
++	case SSH2_AGENTC_SIGN_REQUEST:
++		debug("process_sign");
++		process_sign();
++		break;
++	default:
++		error("Unknown message %d", type);
++		break;
++	}
++	/* discard the remaining bytes from the current packet */
++	if (buf_len < buffer_len(&iqueue)) {
++		error("iqueue grew unexpectedly");
++		cleanup_exit(255);
++	}
++	consumed = buf_len - buffer_len(&iqueue);
++	if (msg_len < consumed) {
++		error("msg_len %d < consumed %d", msg_len, consumed);
++		cleanup_exit(255);
++	}
++	if (msg_len > consumed)
++		buffer_consume(&iqueue, msg_len - consumed);
++}
++
++void
++cleanup_exit(int i)
++{
++	/* XXX */
++	_exit(i);
++}
++
++int
++main(int argc, char **argv)
++{
++	fd_set *rset, *wset;
++	int in, out, max, log_stderr = 0;
++	ssize_t len, olen, set_size;
++	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
++	LogLevel log_level = SYSLOG_LEVEL_ERROR;
++	char buf[4*4096];
++
++	TAILQ_INIT(&pkcs11_keylist);
++	pkcs11_init(0);
++
++	extern char *optarg;
++	extern char *__progname;
++
++	log_init(__progname, log_level, log_facility, log_stderr);
++
++	in = STDIN_FILENO;
++	out = STDOUT_FILENO;
++
++	max = 0;
++	if (in > max)
++		max = in;
++	if (out > max)
++		max = out;
++
++	buffer_init(&iqueue);
++	buffer_init(&oqueue);
++
++	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
++	rset = (fd_set *)xmalloc(set_size);
++	wset = (fd_set *)xmalloc(set_size);
++
++	for (;;) {
++		memset(rset, 0, set_size);
++		memset(wset, 0, set_size);
++
++		/*
++		 * Ensure that we can read a full buffer and handle
++		 * the worst-case length packet it can generate,
++		 * otherwise apply backpressure by stopping reads.
++		 */
++		if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
++		    buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
++			FD_SET(in, rset);
++
++		olen = buffer_len(&oqueue);
++		if (olen > 0)
++			FD_SET(out, wset);
++
++		if (select(max+1, rset, wset, NULL, NULL) < 0) {
++			if (errno == EINTR)
++				continue;
++			error("select: %s", strerror(errno));
++			cleanup_exit(2);
++		}
++
++		/* copy stdin to iqueue */
++		if (FD_ISSET(in, rset)) {
++			len = read(in, buf, sizeof buf);
++			if (len == 0) {
++				debug("read eof");
++				cleanup_exit(0);
++			} else if (len < 0) {
++				error("read: %s", strerror(errno));
++				cleanup_exit(1);
++			} else {
++				buffer_append(&iqueue, buf, len);
++			}
++		}
++		/* send oqueue to stdout */
++		if (FD_ISSET(out, wset)) {
++			len = write(out, buffer_ptr(&oqueue), olen);
++			if (len < 0) {
++				error("write: %s", strerror(errno));
++				cleanup_exit(1);
++			} else {
++				buffer_consume(&oqueue, len);
++			}
++		}
++
++		/*
++		 * Process requests from client if we can fit the results
++		 * into the output buffer, otherwise stop processing input
++		 * and let the output queue drain.
++		 */
++		if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH))
++			process();
++	}
++}
+diff -up openssh-5.3p1/ssh-pkcs11.h.pkcs11 openssh-5.3p1/ssh-pkcs11.h
+--- openssh-5.3p1/ssh-pkcs11.h.pkcs11	2013-07-10 14:34:46.517673757 +0200
++++ openssh-5.3p1/ssh-pkcs11.h	2013-07-10 14:34:46.518673757 +0200
+@@ -0,0 +1,19 @@
++/*
++ * Copyright (c) 2010 Markus Friedl.  All rights reserved.
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++int	pkcs11_init(int);
++void	pkcs11_terminate(void);
++int	pkcs11_add_provider(char *, char *, Key ***);
++int	pkcs11_del_provider(char *);
+diff -up openssh-5.3p1/ssh.c.pkcs11 openssh-5.3p1/ssh.c
+--- openssh-5.3p1/ssh.c.pkcs11	2013-09-06 16:26:16.469002501 +0200
++++ openssh-5.3p1/ssh.c	2013-09-06 16:44:58.571825806 +0200
+@@ -105,8 +105,8 @@
+ #include "uidswap.h"
+ #include "version.h"
+ 
+-#ifdef SMARTCARD
+-#include "scard.h"
++#ifdef ENABLE_PKCS11
++#include "ssh-pkcs11.h"
+ #endif
+ #ifdef HAVE_LIBNSS
+ #include "nsskeys.h"
+@@ -192,7 +192,8 @@ usage(void)
+ 	fprintf(stderr,
+ "usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n"
+ "           [-D [bind_address:]port] [-e escape_char] [-F configfile]\n"
+-"           [-i identity_file] [-L [bind_address:]port:host:hostport]\n"
++"           [-I pkcs11] [-i identity_file]\n"
++"           [-L [bind_address:]port:host:hostport]\n"
+ "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
+ "           [-R [bind_address:]port:host:hostport] [-S ctl_path]\n"
+ "           [-W host:port] [-w local_tun[:remote_tun]]\n"
+@@ -369,10 +370,10 @@ main(int ac, char **av)
+ 			    xstrdup(optarg);
+ 			break;
+ 		case 'I':
+-#ifdef SMARTCARD
+-			options.smartcard_device = xstrdup(optarg);
++#ifdef ENABLE_PKCS11
++			options.pkcs11_provider = xstrdup(optarg);
+ #else
+-			fprintf(stderr, "no support for smartcards.\n");
++			fprintf(stderr, "no support for PKCS#11.\n");
+ #endif
+ 			break;
+ 		case 't':
+@@ -1315,52 +1316,51 @@ load_public_identity_files(void)
+ 	int i = 0;
+ 	Key *public;
+ 	struct passwd *pw;
+-#if defined(SMARTCARD) || defined(HAVE_LIBNSS)
++	u_int n_ids;
++	char *identity_files[SSH_MAX_IDENTITY_FILES];
++	Key *identity_keys[SSH_MAX_IDENTITY_FILES];
++#ifdef ENABLE_PKCS11
+ 	Key **keys;
+-#endif
++	int nkeys;
++#endif /* PKCS11 */
++
++	n_ids = 0;
++	bzero(identity_files, sizeof(identity_files));
++	bzero(identity_keys, sizeof(identity_keys));
+ 
+-#ifdef SMARTCARD
+-	if (options.smartcard_device != NULL &&
++#ifdef ENABLE_PKCS11
++	if (options.pkcs11_provider != NULL &&
+ 	    options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
+-	    (keys = sc_get_keys(options.smartcard_device, NULL)) != NULL) {
+-		int count = 0;
+-		for (i = 0; keys[i] != NULL; i++) {
+-			count++;
+-			memmove(&options.identity_files[1],
+-			    &options.identity_files[0],
+-			    sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1));
+-			memmove(&options.identity_keys[1],
+-			    &options.identity_keys[0],
+-			    sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1));
+-			options.num_identity_files++;
+-			options.identity_keys[0] = keys[i];
+-			options.identity_files[0] = sc_get_key_label(keys[i]);
++	    (pkcs11_init(!options.batch_mode) == 0) &&
++	    (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL,
++	    &keys)) > 0) {
++		for (i = 0; i < nkeys; i++) {
++			if (n_ids >= SSH_MAX_IDENTITY_FILES) {
++				key_free(keys[i]);
++				continue;
++			}
++			identity_keys[n_ids] = keys[i];
++			identity_files[n_ids] =
++			    xstrdup(options.pkcs11_provider); /* XXX */
++			n_ids++;
+ 		}
+-		if (options.num_identity_files > SSH_MAX_IDENTITY_FILES)
+-			options.num_identity_files = SSH_MAX_IDENTITY_FILES;
+-		i = count;
+-		xfree(keys);
++		free(keys);
+ 	}
+-#endif /* SMARTCARD */
++#endif /* ENABLE_PKCS11 */
+ #ifdef HAVE_LIBNSS
+-	if (options.use_nss &&
+-	    options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
++	if (options.use_nss && n_ids < SSH_MAX_IDENTITY_FILES &&
+ 	    (keys = nss_get_keys(options.nss_token, NULL, NULL, 
+ 	    	options.number_of_password_prompts, options.num_nss_modules,
+ 	    	options.nss_modules)) != NULL) {
+-		int count;
+-		for (count = 0; keys[count] != NULL; count++) {
+-			memmove(&options.identity_files[1], &options.identity_files[0],
+-			    sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1));
+-			memmove(&options.identity_keys[1], &options.identity_keys[0],
+-			    sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1));
+-			options.num_identity_files++;
+-			options.identity_keys[0] = keys[count];
+-			options.identity_files[0] = nss_get_key_label(keys[count]);
++		for (i = 0; keys[i] != NULL; i++) {
++			if (n_ids >= SSH_MAX_IDENTITY_FILES) {
++				key_free(keys[i]);
++				continue;
++			}
++			identity_keys[n_ids] = keys[i];
++			identity_files[n_ids] = nss_get_key_label(keys[i]);
++			n_ids++;
+ 		}
+-		if (options.num_identity_files > SSH_MAX_IDENTITY_FILES)
+-			options.num_identity_files = SSH_MAX_IDENTITY_FILES;
+-		i += count;
+ 		xfree(keys);
+ 	}
+ #endif /* HAVE_LIBNSS */
+@@ -1372,7 +1372,12 @@ load_public_identity_files(void)
+ 	if (gethostname(thishost, sizeof(thishost)) == -1)
+ 		fatal("load_public_identity_files: gethostname: %s",
+ 		    strerror(errno));
+-	for (; i < options.num_identity_files; i++) {
++	for (i = 0; i < options.num_identity_files; i++) {
++		if (n_ids >= SSH_MAX_IDENTITY_FILES ||
++		    strcasecmp(options.identity_files[i], "none") == 0) {
++			free(options.identity_files[i]);
++			continue;
++		}
+ 		cp = tilde_expand_filename(options.identity_files[i],
+ 		    original_real_uid);
+ 		filename = percent_expand(cp, "d", pwdir,
+@@ -1383,9 +1388,15 @@ load_public_identity_files(void)
+ 		debug("identity file %s type %d", filename,
+ 		    public ? public->type : -1);
+ 		xfree(options.identity_files[i]);
+-		options.identity_files[i] = filename;
+-		options.identity_keys[i] = public;
+-	}
++		identity_files[n_ids] = filename;
++		identity_keys[n_ids] = public;
++		if (++n_ids >= SSH_MAX_IDENTITY_FILES)
++			continue;
++	}
++	options.num_identity_files = n_ids;
++	memcpy(options.identity_files, identity_files, sizeof(identity_files));
++	memcpy(options.identity_keys, identity_keys, sizeof(identity_keys));
++
+ 	bzero(pwname, strlen(pwname));
+ 	xfree(pwname);
+ 	bzero(pwdir, strlen(pwdir));
diff --git a/openssh-5.3p1-ssh-agent-fix-race.patch b/openssh-5.3p1-ssh-agent-fix-race.patch
new file mode 100644
index 0000000..4fed42c
--- /dev/null
+++ b/openssh-5.3p1-ssh-agent-fix-race.patch
@@ -0,0 +1,67 @@
+diff -up openssh-5.3p1/ssh-agent.c.ssh-agent-race openssh-5.3p1/ssh-agent.c
+--- openssh-5.3p1/ssh-agent.c.ssh-agent-race	2013-01-15 13:31:33.430158950 +0100
++++ openssh-5.3p1/ssh-agent.c	2013-01-15 13:31:33.539158541 +0100
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: ssh-agent.c,v 1.161 2009/03/23 19:38:04 tobias Exp $ */
++/* $OpenBSD: ssh-agent.c,v 1.162 2009/09/01 14:43:17 djm Exp $ */
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
+@@ -1041,11 +1041,11 @@ after_select(fd_set *readset, fd_set *wr
+ 	socklen_t slen;
+ 	char buf[1024];
+ 	int len, sock;
+-	u_int i;
++	u_int i, orig_alloc;
+ 	uid_t euid;
+ 	gid_t egid;
+ 
+-	for (i = 0; i < sockets_alloc; i++)
++	for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++)
+ 		switch (sockets[i].type) {
+ 		case AUTH_UNUSED:
+ 			break;
+@@ -1078,16 +1078,13 @@ after_select(fd_set *readset, fd_set *wr
+ 		case AUTH_CONNECTION:
+ 			if (buffer_len(&sockets[i].output) > 0 &&
+ 			    FD_ISSET(sockets[i].fd, writeset)) {
+-				do {
+-					len = write(sockets[i].fd,
+-					    buffer_ptr(&sockets[i].output),
+-					    buffer_len(&sockets[i].output));
+-					if (len == -1 && (errno == EAGAIN ||
+-					    errno == EINTR ||
+-					    errno == EWOULDBLOCK))
+-						continue;
+-					break;
+-				} while (1);
++				len = write(sockets[i].fd,
++				    buffer_ptr(&sockets[i].output),
++				    buffer_len(&sockets[i].output));
++				if (len == -1 && (errno == EAGAIN ||
++				    errno == EWOULDBLOCK ||
++				    errno == EINTR))
++					continue;
+ 				if (len <= 0) {
+ 					close_socket(&sockets[i]);
+ 					break;
+@@ -1095,14 +1092,11 @@ after_select(fd_set *readset, fd_set *wr
+ 				buffer_consume(&sockets[i].output, len);
+ 			}
+ 			if (FD_ISSET(sockets[i].fd, readset)) {
+-				do {
+-					len = read(sockets[i].fd, buf, sizeof(buf));
+-					if (len == -1 && (errno == EAGAIN ||
+-					    errno == EINTR ||
+-					    errno == EWOULDBLOCK))
+-						continue;
+-					break;
+-				} while (1);
++				len = read(sockets[i].fd, buf, sizeof(buf));
++				if (len == -1 && (errno == EAGAIN ||
++				    errno == EWOULDBLOCK ||
++				    errno == EINTR))
++					continue;
+ 				if (len <= 0) {
+ 					close_socket(&sockets[i]);
+ 					break;
diff --git a/openssh-5.3p1-ssh-certificates.patch b/openssh-5.3p1-ssh-certificates.patch
new file mode 100644
index 0000000..5f1a692
--- /dev/null
+++ b/openssh-5.3p1-ssh-certificates.patch
@@ -0,0 +1,6741 @@
+diff -up openssh-5.3p1/addrmatch.c.certificates openssh-5.3p1/addrmatch.c
+--- openssh-5.3p1/addrmatch.c.certificates	2009-01-28 06:16:00.000000000 +0100
++++ openssh-5.3p1/addrmatch.c	2013-07-18 15:38:20.429186442 +0200
+@@ -1,4 +1,4 @@
+-/*	$OpenBSD: addrmatch.c,v 1.4 2008/12/10 03:55:20 stevesk Exp $ */
++/*	$OpenBSD: addrmatch.c,v 1.5 2010/02/26 20:29:54 djm Exp $ */
+ 
+ /*
+  * Copyright (c) 2004-2008 Damien Miller <djm at mindrot.org>
+@@ -126,6 +126,8 @@ addr_netmask(int af, u_int l, struct xad
+ 	switch (af) {
+ 	case AF_INET:
+ 		n->af = AF_INET;
++		if (l == 0)
++			return 0;
+ 		n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff);
+ 		return 0;
+ 	case AF_INET6:
+@@ -419,6 +421,80 @@ addr_match_list(const char *addr, const
+ 		}
+ 	}
+ 	xfree(o);
++
++	return ret;
++}
++
++/*
++ * Match "addr" against list CIDR list "_list". Lexical wildcards and
++ * negation are not supported. If "addr" == NULL, will verify structure
++ * of "_list".
++ *
++ * Returns 1 on match found (never returned when addr == NULL).
++ * Returns 0 on if no match found, or no errors found when addr == NULL.
++ * Returns -1 on error
++ */
++int
++addr_match_cidr_list(const char *addr, const char *_list)
++{
++	char *list, *cp, *o;
++	struct xaddr try_addr, match_addr;
++	u_int masklen;
++	int ret = 0, r;
++
++	if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
++		debug2("%s: couldn't parse address %.100s", __func__, addr);
++		return 0;
++	}
++	if ((o = list = strdup(_list)) == NULL)
++		return -1;
++	while ((cp = strsep(&list, ",")) != NULL) {
++		if (*cp == '\0') {
++			error("%s: empty entry in list \"%.100s\"",
++			    __func__, o);
++			ret = -1;
++			break;
++		}
++
++		/*
++		 * NB. This function is called in pre-auth with untrusted data,
++		 * so be extra paranoid about junk reaching getaddrino (via
++		 * addr_pton_cidr).
++		 */
++
++		/* Stop junk from reaching getaddrinfo. +3 is for masklen */
++		if (strlen(cp) > INET6_ADDRSTRLEN + 3) {
++			error("%s: list entry \"%.100s\" too long",
++			    __func__, cp);
++			ret = -1;
++			break;
++		}
++#define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/"
++		if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) {
++			error("%s: list entry \"%.100s\" contains invalid "
++			    "characters", __func__, cp);
++			ret = -1;
++		}
++
++		/* Prefer CIDR address matching */
++		r = addr_pton_cidr(cp, &match_addr, &masklen);
++		if (r == -1) {
++			error("Invalid network entry \"%.100s\"", cp);
++			ret = -1;
++			break;
++		} else if (r == -2) {
++			error("Inconsistent mask length for "
++			    "network \"%.100s\"", cp);
++			ret = -1;
++			break;
++		} else if (r == 0 && addr != NULL) {
++			if (addr_netmatch(&try_addr, &match_addr,
++			    masklen) == 0)
++				ret = 1;
++			continue;
++		}
++	}
++	xfree(o);
+ 
+ 	return ret;
+ }
+diff -up openssh-5.3p1/auth2-hostbased.c.certificates openssh-5.3p1/auth2-hostbased.c
+--- openssh-5.3p1/auth2-hostbased.c.certificates	2013-07-18 15:38:20.331186869 +0200
++++ openssh-5.3p1/auth2-hostbased.c	2013-07-18 15:38:20.429186442 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: auth2-hostbased.c,v 1.12 2008/07/17 08:51:07 djm Exp $ */
++/* $OpenBSD: auth2-hostbased.c,v 1.13 2010/03/04 10:36:03 djm Exp $ */
+ /*
+  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+  *
+@@ -165,6 +165,9 @@ hostbased_key_allowed(struct passwd *pw,
+ 	HostStatus host_status;
+ 	int len;
+ 
++	if (auth_key_is_revoked(key))
++		return 0;
++
+ 	resolvedname = get_canonical_hostname(options.use_dns);
+ 	ipaddr = get_remote_ipaddr();
+ 
+diff -up openssh-5.3p1/auth2-pubkey.c.certificates openssh-5.3p1/auth2-pubkey.c
+--- openssh-5.3p1/auth2-pubkey.c.certificates	2013-07-18 15:38:20.358186751 +0200
++++ openssh-5.3p1/auth2-pubkey.c	2013-07-18 15:38:20.429186442 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: auth2-pubkey.c,v 1.19 2008/07/03 21:46:58 otto Exp $ */
++/* $OpenBSD: auth2-pubkey.c,v 1.25 2010/05/20 11:25:26 djm Exp $ */
+ /*
+  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+  *
+@@ -33,6 +33,8 @@
+ #include <pwd.h>
+ #include <stdio.h>
+ #include <stdarg.h>
++#include <string.h>
++#include <time.h>
+ #include <unistd.h>
+ #include <openssl/fips.h>
+ 
+@@ -56,6 +58,8 @@
+ #endif
+ #include "monitor_wrap.h"
+ #include "misc.h"
++#include "authfile.h"
++#include "match.h"
+ 
+ /* import */
+ extern ServerOptions options;
+@@ -183,6 +187,63 @@ done:
+ 	return authenticated;
+ }
+ 
++static int
++match_principals_option(const char *principal_list, struct KeyCert *cert)
++{
++	char *result;
++	u_int i;
++
++	/* XXX percent_expand() sequences for authorized_principals? */
++
++	for (i = 0; i < cert->nprincipals; i++) {
++		if ((result = match_list(cert->principals[i],
++		    principal_list, NULL)) != NULL) {
++			debug3("matched principal from key options \"%.100s\"",
++			    result);
++			xfree(result);
++			return 1;
++		}
++	}
++	return 0;
++}
++
++static int
++match_principals_file(const char *file, struct passwd *pw, struct KeyCert *cert)
++{
++	FILE *f;
++	char line[SSH_MAX_PUBKEY_BYTES], *cp;
++	u_long linenum = 0;
++	u_int i;
++
++	temporarily_use_uid(pw);
++	debug("trying authorized principals file %s", file);
++	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
++		restore_uid();
++		return 0;
++	}
++	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
++		/* Skip leading whitespace, empty and comment lines. */
++		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
++			;
++		if (!*cp || *cp == '\n' || *cp == '#')
++			continue;
++		line[strcspn(line, "\n")] = '\0';
++
++		for (i = 0; i < cert->nprincipals; i++) {
++			if (strcmp(cp, cert->principals[i]) == 0) {
++				debug3("matched principal from file \"%.100s\"",
++			    	    cert->principals[i]);
++				fclose(f);
++				restore_uid();
++				return 1;
++			}
++		}
++	}
++	fclose(f);
++	restore_uid();
++	return 0;
++}
++
+ int
+ user_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen)
+ {
+@@ -200,17 +261,20 @@ static int
+ user_search_key_in_file(FILE *f, char *file, Key* key, struct passwd *pw)
+ {
+ 	char line[SSH_MAX_PUBKEY_BYTES];
++	const char *reason;
+ 	int found_key = 0;
+ 	u_long linenum = 0;
+ 	Key *found;
+ 	char *fp;
+ 
+ 	found_key = 0;
+-	found = key_new(key->type);
++	found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
+ 
+ 	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
+ 		char *cp, *key_options = NULL;
+ 
++		auth_clear_options();
++
+ 		/* Skip leading whitespace, empty and comment lines. */
+ 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
+ 			;
+@@ -237,8 +301,54 @@ user_search_key_in_file(FILE *f, char *f
+ 				continue;
+ 			}
+ 		}
+-		if (key_equal(found, key) &&
+-		    auth_parse_options(pw, key_options, file, linenum) == 1) {
++		if (key_is_cert(key)) {
++			if (!key_equal(found, key->cert->signature_key))
++				continue;
++			if (auth_parse_options(pw, key_options, file,
++			    linenum) != 1)
++				continue;
++			if (!key_is_cert_authority)
++				continue;
++			fp = key_fingerprint(found, FIPS_mode() ? SSH_FP_SHA1 : SSH_FP_MD5,
++			    SSH_FP_HEX);
++			debug("matching CA found: file %s, line %lu, %s %s",
++			    file, linenum, key_type(found), fp);
++			/*
++			 * If the user has specified a list of principals as
++			 * a key option, then prefer that list to matching
++			 * their username in the certificate principals list.
++			 */
++			if (authorized_principals != NULL &&
++			    !match_principals_option(authorized_principals,
++			    key->cert)) {
++				reason = "Certificate does not contain an "
++				    "authorized principal";
++ fail_reason:
++				xfree(fp);
++				error("%s", reason);
++				auth_debug_add("%s", reason);
++				continue;
++			}
++			if (key_cert_check_authority(key, 0, 0,
++			    authorized_principals == NULL ? pw->pw_name : NULL,
++			    &reason) != 0)
++				goto fail_reason;
++			if (auth_cert_options(key, pw) != 0) {
++				xfree(fp);
++				continue;
++			}
++			verbose("Accepted certificate ID \"%s\" "
++			    "signed by %s CA %s via %s", key->cert->key_id,
++			    key_type(found), fp, file);
++			xfree(fp);
++			found_key = 1;
++			break;
++		} else if (key_equal(found, key)) {
++			if (auth_parse_options(pw, key_options, file,
++			    linenum) != 1)
++				continue;
++			if (key_is_cert_authority)
++				continue;
+ 			found_key = 1;
+ 			debug("matching key found: file %s, line %lu",
+ 			    file, linenum);
+@@ -435,6 +545,61 @@ go_away:
+ }
+ #endif
+ 
++/* Authenticate a certificate key against TrustedUserCAKeys */
++static int
++user_cert_trusted_ca(struct passwd *pw, Key *key)
++{
++	char *ca_fp, *principals_file = NULL;
++	const char *reason;
++	int ret = 0;
++
++	if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
++		return 0;
++
++	ca_fp = key_fingerprint(key->cert->signature_key,
++	    FIPS_mode() ? SSH_FP_SHA1 : SSH_FP_MD5, SSH_FP_HEX);
++
++	if (key_in_file(key->cert->signature_key,
++	    options.trusted_user_ca_keys, 1) != 1) {
++		debug2("%s: CA %s %s is not listed in %s", __func__,
++		    key_type(key->cert->signature_key), ca_fp,
++		    options.trusted_user_ca_keys);
++		goto out;
++	}
++	/*
++	 * If AuthorizedPrincipals is in use, then compare the certificate
++	 * principals against the names in that file rather than matching
++	 * against the username.
++	 */
++	if ((principals_file = authorized_principals_file(pw)) != NULL) {
++		if (!match_principals_file(principals_file, pw, key->cert)) {
++			reason = "Certificate does not contain an "
++			    "authorized principal";
++ fail_reason:
++			error("%s", reason);
++			auth_debug_add("%s", reason);
++			goto out;
++		}
++	}
++	if (key_cert_check_authority(key, 0, 1,
++	    principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
++		goto fail_reason;
++	if (auth_cert_options(key, pw) != 0)
++		goto out;
++
++	verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s",
++	    key->cert->key_id, key_type(key->cert->signature_key), ca_fp,
++	    options.trusted_user_ca_keys);
++	ret = 1;
++
++ out:
++	if (principals_file != NULL)
++		xfree(principals_file);
++	if (ca_fp != NULL)
++		xfree(ca_fp);
++	return ret;
++}
++
+ /* check whether given key is in <AuthorizedKeysCommand or .ssh/authorized_keys* */
+ int
+ user_key_allowed(struct passwd *pw, Key *key)
+@@ -442,6 +607,15 @@ user_key_allowed(struct passwd *pw, Key
+ 	int success;
+ 	char *file;
+ 
++	if (auth_key_is_revoked(key))
++		return 0;
++	if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
++		return 0;
++
++	success = user_cert_trusted_ca(pw, key);
++	if (success)
++		return success;
++
+ #ifdef WITH_AUTHORIZED_KEYS_COMMAND
+ 	success = user_key_via_command_allowed2(pw, key);
+ 	if (success > 0)
+diff -up openssh-5.3p1/auth.c.certificates openssh-5.3p1/auth.c
+--- openssh-5.3p1/auth.c.certificates	2013-07-18 15:38:20.385186634 +0200
++++ openssh-5.3p1/auth.c	2013-07-18 15:38:20.430186438 +0200
+@@ -69,6 +69,7 @@
+ #ifdef GSSAPI
+ #include "ssh-gss.h"
+ #endif
++#include "authfile.h"
+ #include "monitor_wrap.h"
+ 
+ /* import */
+@@ -362,6 +363,14 @@ authorized_keys_file2(struct passwd *pw)
+ 	return expand_authorized_keys(options.authorized_keys_file2, pw);
+ }
+ 
++char *
++authorized_principals_file(struct passwd *pw)
++{
++	if (options.authorized_principals_file == NULL)
++		return NULL;
++	return expand_authorized_keys(options.authorized_principals_file, pw);
++}
++
+ /* return ok if key exists in sysfile or userfile */
+ HostStatus
+ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
+@@ -473,28 +482,29 @@ secure_filename(FILE *f, const char *fil
+ 	return 0;
+ }
+ 
+-FILE *
+-auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
++static FILE *
++auth_openfile(const char *file, struct passwd *pw, int strict_modes,
++    int log_missing, char *file_type)
+ {
+ 	char line[1024];
+ 	struct stat st;
+ 	int fd;
+ 	FILE *f;
+ 
+-	/*
+-	 * Open the file containing the authorized keys
+-	 * Fail quietly if file does not exist
+-	 */
+-	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1)
++	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
++		if (log_missing || errno != ENOENT)
++			debug("Could not open %s '%s': %s", file_type, file,
++			  strerror(errno));
+ 		return NULL;
++	}
+ 
+ 	if (fstat(fd, &st) < 0) {
+ 		close(fd);
+ 		return NULL;
+ 	}
+ 	if (!S_ISREG(st.st_mode)) {
+-		logit("User %s authorized keys %s is not a regular file",
+-		    pw->pw_name, file);
++		logit("User %s %s %s is not a regular file",
++		    pw->pw_name, file_type, file);
+ 		close(fd);
+ 		return NULL;
+ 	}
+@@ -513,6 +523,20 @@ auth_openkeyfile(const char *file, struc
+ 	return f;
+ }
+ 
++
++FILE *
++auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
++{
++	return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
++}
++
++FILE *
++auth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
++{
++	return auth_openfile(file, pw, strict_modes, 0,
++	    "authorized principals");
++}
++
+ struct passwd *
+ getpwnamallow(const char *user)
+ {
+@@ -562,6 +586,34 @@ getpwnamallow(const char *user)
+ 	return (NULL);
+ }
+ 
++/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */
++int
++auth_key_is_revoked(Key *key)
++{
++	char *key_fp;
++
++	if (options.revoked_keys_file == NULL)
++		return 0;
++
++	switch (key_in_file(key, options.revoked_keys_file, 0)) {
++	case 0:
++		/* key not revoked */
++		return 0;
++	case -1:
++		/* Error opening revoked_keys_file: refuse all keys */
++		error("Revoked keys file is unreadable: refusing public key "
++		    "authentication");
++		return 1;
++	case 1:
++		/* Key revoked */
++		key_fp = key_fingerprint(key, FIPS_mode() ? SSH_FP_SHA1 : SSH_FP_MD5, SSH_FP_HEX);
++		error("WARNING: authentication attempt with a revoked %s key %s", key_type(key), key_fp);
++		xfree(key_fp);
++		return 1;
++	}
++	fatal("key_in_file returned junk");
++}
++
+ void
+ auth_debug_add(const char *fmt,...)
+ {
+diff -up openssh-5.3p1/authfd.c.certificates openssh-5.3p1/authfd.c
+--- openssh-5.3p1/authfd.c.certificates	2013-07-18 15:38:20.277187104 +0200
++++ openssh-5.3p1/authfd.c	2013-07-18 15:38:20.430186438 +0200
+@@ -483,6 +483,17 @@ ssh_encode_identity_ssh2(Buffer *b, Key
+ 		buffer_put_bignum2(b, key->rsa->p);
+ 		buffer_put_bignum2(b, key->rsa->q);
+ 		break;
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
++		if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
++			fatal("%s: no cert/certblob", __func__);
++		buffer_put_string(b, buffer_ptr(&key->cert->certblob),
++		    buffer_len(&key->cert->certblob));
++		buffer_put_bignum2(b, key->rsa->d);
++		buffer_put_bignum2(b, key->rsa->iqmp);
++		buffer_put_bignum2(b, key->rsa->p);
++		buffer_put_bignum2(b, key->rsa->q);
++		break;
+ 	case KEY_DSA:
+ 		buffer_put_bignum2(b, key->dsa->p);
+ 		buffer_put_bignum2(b, key->dsa->q);
+@@ -490,6 +501,14 @@ ssh_encode_identity_ssh2(Buffer *b, Key
+ 		buffer_put_bignum2(b, key->dsa->pub_key);
+ 		buffer_put_bignum2(b, key->dsa->priv_key);
+ 		break;
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
++		if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
++			fatal("%s: no cert/certblob", __func__);
++		buffer_put_string(b, buffer_ptr(&key->cert->certblob),
++		    buffer_len(&key->cert->certblob));
++		buffer_put_bignum2(b, key->dsa->priv_key);
++		break;
+ 	}
+ 	buffer_put_cstring(b, comment);
+ }
+@@ -517,7 +536,11 @@ ssh_add_identity_constrained(Authenticat
+ 		ssh_encode_identity_rsa1(&msg, key->rsa, comment);
+ 		break;
+ 	case KEY_RSA:
++	case KEY_RSA_CERT:
++	case KEY_RSA_CERT_V00:
+ 	case KEY_DSA:
++	case KEY_DSA_CERT:
++	case KEY_DSA_CERT_V00:
+ 		type = constrained ?
+ 		    SSH2_AGENTC_ADD_ID_CONSTRAINED :
+ 		    SSH2_AGENTC_ADD_IDENTITY;
+@@ -571,7 +594,8 @@ ssh_remove_identity(AuthenticationConnec
+ 		buffer_put_int(&msg, BN_num_bits(key->rsa->n));
+ 		buffer_put_bignum(&msg, key->rsa->e);
+ 		buffer_put_bignum(&msg, key->rsa->n);
+-	} else if (key->type == KEY_DSA || key->type == KEY_RSA) {
++	} else if (key_type_plain(key->type) == KEY_DSA ||
++	    key_type_plain(key->type) == KEY_RSA) {
+ 		key_to_blob(key, &blob, &blen);
+ 		buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY);
+ 		buffer_put_string(&msg, blob, blen);
+diff -up openssh-5.3p1/authfile.c.certificates openssh-5.3p1/authfile.c
+--- openssh-5.3p1/authfile.c.certificates	2013-07-18 15:38:20.402186559 +0200
++++ openssh-5.3p1/authfile.c	2013-07-18 15:38:20.430186438 +0200
+@@ -689,3 +689,65 @@ key_load_public(const char *filename, ch
+ 	key_free(pub);
+ 	return NULL;
+ }
++
++/*
++ * Returns 1 if the specified "key" is listed in the file "filename",
++ * 0 if the key is not listed or -1 on error.
++ * If strict_type is set then the key type must match exactly,
++ * otherwise a comparison that ignores certficiate data is performed.
++ */
++int
++key_in_file(Key *key, const char *filename, int strict_type)
++{
++	FILE *f;
++	char line[SSH_MAX_PUBKEY_BYTES];
++	char *cp;
++	u_long linenum = 0;
++	int ret = 0;
++	Key *pub;
++	int (*key_compare)(const Key *, const Key *) = strict_type ?
++	    key_equal : key_equal_public;
++
++	if ((f = fopen(filename, "r")) == NULL) {
++		if (errno == ENOENT) {
++			debug("%s: keyfile \"%s\" missing", __func__, filename);
++			return 0;
++		} else {
++			error("%s: could not open keyfile \"%s\": %s", __func__,
++			    filename, strerror(errno));
++			return -1;
++		}
++	}
++
++	while (read_keyfile_line(f, filename, line, sizeof(line),
++		    &linenum) != -1) {
++		cp = line;
++
++		/* Skip leading whitespace. */
++		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
++			;
++
++		/* Skip comments and empty lines */
++		switch (*cp) {
++		case '#':
++		case '\n':
++		case '\0':
++			continue;
++		}
++
++		pub = key_new(KEY_UNSPEC);
++		if (key_read(pub, &cp) != 1) {
++			key_free(pub);
++			continue;
++		}
++		if (key_compare(key, pub)) {
++			ret = 1;
++			key_free(pub);
++			break;
++		}
++		key_free(pub);
++	}
++	fclose(f);
++	return ret;
++}
++
+diff -up openssh-5.3p1/authfile.h.certificates openssh-5.3p1/authfile.h
+--- openssh-5.3p1/authfile.h.certificates	2006-05-06 09:41:51.000000000 +0200
++++ openssh-5.3p1/authfile.h	2013-07-18 15:38:20.430186438 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: authfile.h,v 1.13 2006/04/25 08:02:27 dtucker Exp $ */
++/* $OpenBSD: authfile.h,v 1.14 2010/03/04 10:36:03 djm Exp $ */
+ 
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+@@ -22,5 +22,6 @@ Key	*key_load_private(const char *, cons
+ Key	*key_load_private_type(int, const char *, const char *, char **, int *);
+ Key	*key_load_private_pem(int, int, const char *, char **);
+ int	 key_perm_ok(int, const char *);
++int	 key_in_file(Key *, const char *, int);
+ 
+ #endif
+diff -up openssh-5.3p1/auth.h.certificates openssh-5.3p1/auth.h
+--- openssh-5.3p1/auth.h.certificates	2013-07-18 15:38:20.385186634 +0200
++++ openssh-5.3p1/auth.h	2013-07-18 15:38:20.431186433 +0200
+@@ -174,9 +174,12 @@ void	abandon_challenge_response(Authctxt
+ 
+ char	*authorized_keys_file(struct passwd *);
+ char	*authorized_keys_file2(struct passwd *);
++char	*authorized_principals_file(struct passwd *);
+ int	 user_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
+ 
+ FILE	*auth_openkeyfile(const char *, struct passwd *, int);
++FILE	*auth_openprincipals(const char *, struct passwd *, int);
++int     auth_key_is_revoked(Key *);
+ 
+ HostStatus
+ check_key_in_hostfiles(struct passwd *, Key *, const char *,
+@@ -184,7 +187,8 @@ check_key_in_hostfiles(struct passwd *,
+ 
+ /* hostkey handling */
+ Key	*get_hostkey_by_index(int);
+-Key	*get_hostkey_by_type(int);
++Key	*get_hostkey_public_by_type(int);
++Key	*get_hostkey_private_by_type(int);
+ int	 get_hostkey_index(Key *);
+ int	 ssh1_session_key(BIGNUM *);
+ int	 hostbased_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
+diff -up openssh-5.3p1/auth-options.c.certificates openssh-5.3p1/auth-options.c
+--- openssh-5.3p1/auth-options.c.certificates	2009-01-28 06:33:01.000000000 +0100
++++ openssh-5.3p1/auth-options.c	2013-07-18 15:38:20.431186433 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: auth-options.c,v 1.44 2009/01/22 10:09:16 djm Exp $ */
++/* $OpenBSD: auth-options.c,v 1.46 2010/03/03 01:44:36 djm Exp $ */
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
+@@ -27,10 +27,10 @@
+ #include "canohost.h"
+ #include "buffer.h"
+ #include "channels.h"
+-#include "auth-options.h"
+ #include "servconf.h"
+ #include "misc.h"
+ #include "key.h"
++#include "auth-options.h"
+ #include "hostfile.h"
+ #include "auth.h"
+ #ifdef GSSAPI
+@@ -44,6 +44,7 @@ int no_agent_forwarding_flag = 0;
+ int no_x11_forwarding_flag = 0;
+ int no_pty_flag = 0;
+ int no_user_rc = 0;
++int key_is_cert_authority = 0;
+ 
+ /* "command=" option. */
+ char *forced_command = NULL;
+@@ -54,6 +55,9 @@ struct envstring *custom_environment = N
+ /* "tunnel=" option. */
+ int forced_tun_device = -1;
+ 
++/* "principals=" option. */
++char *authorized_principals = NULL;
++
+ extern ServerOptions options;
+ 
+ void
+@@ -64,6 +68,7 @@ auth_clear_options(void)
+ 	no_pty_flag = 0;
+ 	no_x11_forwarding_flag = 0;
+ 	no_user_rc = 0;
++	key_is_cert_authority = 0;
+ 	while (custom_environment) {
+ 		struct envstring *ce = custom_environment;
+ 		custom_environment = ce->next;
+@@ -74,6 +79,10 @@ auth_clear_options(void)
+ 		xfree(forced_command);
+ 		forced_command = NULL;
+ 	}
++	if (authorized_principals) {
++		xfree(authorized_principals);
++		authorized_principals = NULL;
++	}
+ 	forced_tun_device = -1;
+ 	channel_clear_permitted_opens();
+ 	auth_debug_reset();
+@@ -96,6 +105,12 @@ auth_parse_options(struct passwd *pw, ch
+ 		return 1;
+ 
+ 	while (*opts && *opts != ' ' && *opts != '\t') {
++		cp = "cert-authority";
++		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
++			key_is_cert_authority = 1;
++			opts += strlen(cp);
++			goto next_option;
++		}
+ 		cp = "no-port-forwarding";
+ 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
+ 			auth_debug_add("Port forwarding disabled.");
+@@ -134,6 +149,8 @@ auth_parse_options(struct passwd *pw, ch
+ 		cp = "command=\"";
+ 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
+ 			opts += strlen(cp);
++			if (forced_command != NULL)
++				xfree(forced_command);
+ 			forced_command = xmalloc(strlen(opts) + 1);
+ 			i = 0;
+ 			while (*opts) {
+@@ -160,6 +177,38 @@ auth_parse_options(struct passwd *pw, ch
+ 			opts++;
+ 			goto next_option;
+ 		}
++		cp = "principals=\"";
++		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
++			opts += strlen(cp);
++			if (authorized_principals != NULL)
++				xfree(authorized_principals);
++			authorized_principals = xmalloc(strlen(opts) + 1);
++			i = 0;
++			while (*opts) {
++				if (*opts == '"')
++					break;
++				if (*opts == '\\' && opts[1] == '"') {
++					opts += 2;
++					authorized_principals[i++] = '"';
++					continue;
++				}
++				authorized_principals[i++] = *opts++;
++			}
++			if (!*opts) {
++				debug("%.100s, line %lu: missing end quote",
++				    file, linenum);
++				auth_debug_add("%.100s, line %lu: missing end quote",
++				    file, linenum);
++				xfree(authorized_principals);
++				authorized_principals = NULL;
++				goto bad_option;
++			}
++			authorized_principals[i] = '\0';
++			auth_debug_add("principals: %.900s",
++			    authorized_principals);
++			opts++;
++			goto next_option;
++		}
+ 		cp = "environment=\"";
+ 		if (options.permit_user_env &&
+ 		    strncasecmp(opts, cp, strlen(cp)) == 0) {
+@@ -374,3 +423,234 @@ bad_option:
+ 	/* deny access */
+ 	return 0;
+ }
++
++#define OPTIONS_CRITICAL	1
++#define OPTIONS_EXTENSIONS	2
++static int
++parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw,
++    u_int which, int crit,
++    int *cert_no_port_forwarding_flag,
++    int *cert_no_agent_forwarding_flag,
++    int *cert_no_x11_forwarding_flag,
++    int *cert_no_pty_flag,
++    int *cert_no_user_rc,
++    char **cert_forced_command,
++    int *cert_source_address_done)
++{
++	char *command, *allowed;
++	const char *remote_ip;
++	u_char *name = NULL, *data_blob = NULL;
++	u_int nlen, dlen, clen;
++	Buffer c, data;
++	int ret = -1, found;
++
++	buffer_init(&data);
++
++	/* Make copy to avoid altering original */
++	buffer_init(&c);
++	buffer_append(&c, optblob, optblob_len);
++
++	while (buffer_len(&c) > 0) {
++		if ((name = buffer_get_string_ret(&c, &nlen)) == NULL ||
++		    (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) {
++			error("Certificate options corrupt");
++			goto out;
++		}
++		buffer_append(&data, data_blob, dlen);
++		debug3("found certificate option \"%.100s\" len %u",
++		    name, dlen);
++		if (strlen(name) != nlen) {
++			error("Certificate constraint name contains \\0");
++			goto out;
++		}
++		found = 0;
++		if ((which & OPTIONS_EXTENSIONS) != 0) {
++			if (strcmp(name, "permit-X11-forwarding") == 0) {
++				*cert_no_x11_forwarding_flag = 0;
++				found = 1;
++			} else if (strcmp(name,
++			    "permit-agent-forwarding") == 0) {
++				*cert_no_agent_forwarding_flag = 0;
++				found = 1;
++			} else if (strcmp(name,
++			    "permit-port-forwarding") == 0) {
++				*cert_no_port_forwarding_flag = 0;
++				found = 1;
++			} else if (strcmp(name, "permit-pty") == 0) {
++				*cert_no_pty_flag = 0;
++				found = 1;
++			} else if (strcmp(name, "permit-user-rc") == 0) {
++				*cert_no_user_rc = 0;
++				found = 1;
++			}
++		}
++		if (!found && (which & OPTIONS_CRITICAL) != 0) {
++			if (strcmp(name, "force-command") == 0) {
++				if ((command = buffer_get_string_ret(&data,
++				    &clen)) == NULL) {
++					error("Certificate constraint \"%s\" "
++					    "corrupt", name);
++					goto out;
++				}
++				if (strlen(command) != clen) {
++					error("force-command constraint "
++					    "contains \\0");
++					goto out;
++				}
++				if (*cert_forced_command != NULL) {
++					error("Certificate has multiple "
++					    "force-command options");
++					xfree(command);
++					goto out;
++				}
++				*cert_forced_command = command;
++				found = 1;
++			}
++			if (strcmp(name, "source-address") == 0) {
++				if ((allowed = buffer_get_string_ret(&data,
++				    &clen)) == NULL) {
++					error("Certificate constraint "
++					    "\"%s\" corrupt", name);
++					goto out;
++				}
++				if (strlen(allowed) != clen) {
++					error("source-address constraint "
++					    "contains \\0");
++					goto out;
++				}
++				if ((*cert_source_address_done)++) {
++					error("Certificate has multiple "
++					    "source-address options");
++					xfree(allowed);
++					goto out;
++				}
++				remote_ip = get_remote_ipaddr();
++				switch (addr_match_cidr_list(remote_ip,
++				    allowed)) {
++				case 1:
++					/* accepted */
++					xfree(allowed);
++					break;
++				case 0:
++					/* no match */
++					logit("Authentication tried for %.100s "
++					    "with valid certificate but not "
++					    "from a permitted host "
++					    "(ip=%.200s).", pw->pw_name,
++					    remote_ip);
++					auth_debug_add("Your address '%.200s' "
++					    "is not permitted to use this "
++					    "certificate for login.",
++					    remote_ip);
++					xfree(allowed);
++					goto out;
++				case -1:
++					error("Certificate source-address "
++					    "contents invalid");
++					xfree(allowed);
++					goto out;
++				}
++				found = 1;
++			}
++		}
++
++		if (!found) {
++			if (crit) {
++				error("Certificate critical option \"%s\" "
++				    "is not supported", name);
++				goto out;
++			} else {
++				logit("Certificate extension \"%s\" "
++				    "is not supported", name);
++			}
++		} else if (buffer_len(&data) != 0) {
++			error("Certificate option \"%s\" corrupt "
++			    "(extra data)", name);
++			goto out;
++		}
++		buffer_clear(&data);
++		xfree(name);
++		xfree(data_blob);
++		name = data_blob = NULL;
++	}
++	/* successfully parsed all options */
++	ret = 0;
++
++ out:
++	if (ret != 0 &&
++	    cert_forced_command != NULL &&
++	    *cert_forced_command != NULL) {
++		xfree(*cert_forced_command);
++		*cert_forced_command = NULL;
++	}
++	if (name != NULL)
++		xfree(name);
++	if (data_blob != NULL)
++		xfree(data_blob);
++	buffer_free(&data);
++	buffer_free(&c);
++	return ret;
++}
++
++/*
++ * Set options from critical certificate options. These supersede user key
++ * options so this must be called after auth_parse_options().
++ */
++int
++auth_cert_options(Key *k, struct passwd *pw)
++{
++	int cert_no_port_forwarding_flag = 1;
++	int cert_no_agent_forwarding_flag = 1;
++	int cert_no_x11_forwarding_flag = 1;
++	int cert_no_pty_flag = 1;
++	int cert_no_user_rc = 1;
++	char *cert_forced_command = NULL;
++	int cert_source_address_done = 0;
++
++	if (key_cert_is_legacy(k)) {
++		/* All options are in the one field for v00 certs */
++		if (parse_option_list(buffer_ptr(&k->cert->critical),
++		    buffer_len(&k->cert->critical), pw,
++		    OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1,
++		    &cert_no_port_forwarding_flag,
++		    &cert_no_agent_forwarding_flag,
++		    &cert_no_x11_forwarding_flag,
++		    &cert_no_pty_flag,
++		    &cert_no_user_rc,
++		    &cert_forced_command,
++		    &cert_source_address_done) == -1)
++			return -1;
++	} else {
++		/* Separate options and extensions for v01 certs */
++		if (parse_option_list(buffer_ptr(&k->cert->critical),
++		    buffer_len(&k->cert->critical), pw,
++		    OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
++		    &cert_forced_command,
++		    &cert_source_address_done) == -1)
++			return -1;
++		if (parse_option_list(buffer_ptr(&k->cert->extensions),
++		    buffer_len(&k->cert->extensions), pw,
++		    OPTIONS_EXTENSIONS, 1,
++		    &cert_no_port_forwarding_flag,
++		    &cert_no_agent_forwarding_flag,
++		    &cert_no_x11_forwarding_flag,
++		    &cert_no_pty_flag,
++		    &cert_no_user_rc,
++		    NULL, NULL) == -1)
++			return -1;
++	}
++
++	no_port_forwarding_flag |= cert_no_port_forwarding_flag;
++	no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
++	no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
++	no_pty_flag |= cert_no_pty_flag;
++	no_user_rc |= cert_no_user_rc;
++	/* CA-specified forced command supersedes key option */
++	if (cert_forced_command != NULL) {
++		if (forced_command != NULL)
++			xfree(forced_command);
++		forced_command = cert_forced_command;
++	}
++	return 0;
++}
++
+diff -up openssh-5.3p1/auth-options.h.certificates openssh-5.3p1/auth-options.h
+--- openssh-5.3p1/auth-options.h.certificates	2008-03-27 01:03:05.000000000 +0100
++++ openssh-5.3p1/auth-options.h	2013-07-18 15:38:20.431186433 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: auth-options.h,v 1.17 2008/03/26 21:28:14 djm Exp $ */
++/* $OpenBSD: auth-options.h,v 1.20 2010/05/07 11:30:29 djm Exp $ */
+ 
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+@@ -30,8 +30,11 @@ extern int no_user_rc;
+ extern char *forced_command;
+ extern struct envstring *custom_environment;
+ extern int forced_tun_device;
++extern int key_is_cert_authority;
++extern char *authorized_principals;
+ 
+ int	auth_parse_options(struct passwd *, char *, char *, u_long);
+ void	auth_clear_options(void);
++int	auth_cert_options(Key *, struct passwd *);
+ 
+ #endif
+diff -up openssh-5.3p1/auth-rh-rsa.c.certificates openssh-5.3p1/auth-rh-rsa.c
+--- openssh-5.3p1/auth-rh-rsa.c.certificates	2006-08-05 04:39:39.000000000 +0200
++++ openssh-5.3p1/auth-rh-rsa.c	2013-07-18 15:38:20.431186433 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: auth-rh-rsa.c,v 1.42 2006/08/03 03:34:41 deraadt Exp $ */
++/* $OpenBSD: auth-rh-rsa.c,v 1.43 2010/03/04 10:36:03 djm Exp $ */
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
+@@ -44,6 +44,9 @@ auth_rhosts_rsa_key_allowed(struct passw
+ {
+ 	HostStatus host_status;
+ 
++	if (auth_key_is_revoked(client_host_key))
++		return 0;
++
+ 	/* Check if we would accept it using rhosts authentication. */
+ 	if (!auth_rhosts(pw, cuser))
+ 		return 0;
+diff -up openssh-5.3p1/auth-rsa.c.certificates openssh-5.3p1/auth-rsa.c
+--- openssh-5.3p1/auth-rsa.c.certificates	2013-07-18 15:38:20.331186869 +0200
++++ openssh-5.3p1/auth-rsa.c	2013-07-18 15:38:20.432186429 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: auth-rsa.c,v 1.73 2008/07/02 12:03:51 dtucker Exp $ */
++/* $OpenBSD: auth-rsa.c,v 1.76 2010/05/11 02:58:04 djm Exp $ */
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
+@@ -34,11 +34,11 @@
+ #include "uidswap.h"
+ #include "match.h"
+ #include "buffer.h"
+-#include "auth-options.h"
+ #include "pathnames.h"
+ #include "log.h"
+ #include "servconf.h"
+ #include "key.h"
++#include "auth-options.h"
+ #include "hostfile.h"
+ #include "auth.h"
+ #ifdef GSSAPI
+@@ -97,6 +97,9 @@ auth_rsa_verify_response(Key *key, BIGNU
+ 	char *fp;
+ #endif
+ 
++	if (auth_key_is_revoked(key))
++		return 0;
++
+ 	/* don't allow short keys */
+ 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
+ 		error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits",
+@@ -262,7 +265,8 @@ auth_rsa_key_allowed(struct passwd *pw,
+ 		 */
+ 		if (!auth_parse_options(pw, key_options, file, linenum))
+ 			continue;
+-
++		if (key_is_cert_authority)
++			continue;
+ 		/* break out, this key is allowed */
+ 		allowed = 1;
+ 		break;
+diff -up openssh-5.3p1/bufaux.c.certificates openssh-5.3p1/bufaux.c
+--- openssh-5.3p1/bufaux.c.certificates	2008-06-11 01:35:37.000000000 +0200
++++ openssh-5.3p1/bufaux.c	2013-07-18 15:38:20.432186429 +0200
+@@ -198,14 +198,17 @@ buffer_get_string(Buffer *buffer, u_int
+ }
+ 
+ void *
+-buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr)
++buffer_get_string_ptr_ret(Buffer *buffer, u_int *length_ptr)
+ {
+ 	void *ptr;
+ 	u_int len;
+ 
+-	len = buffer_get_int(buffer);
+-	if (len > 256 * 1024)
+-		fatal("buffer_get_string_ptr: bad string length %u", len);
++	if (buffer_get_int_ret(&len, buffer) != 0)
++	        return NULL;
++	if (len > 256 * 1024) {
++	        error("buffer_get_string_ptr: bad string length %u", len);
++	        return NULL;
++	}
+ 	ptr = buffer_ptr(buffer);
+ 	buffer_consume(buffer, len);
+ 	if (length_ptr)
+@@ -213,6 +216,16 @@ buffer_get_string_ptr(Buffer *buffer, u_
+ 	return (ptr);
+ }
+ 
++void *
++buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr)
++{
++	void *ret;
++	
++	if ((ret = buffer_get_string_ptr_ret(buffer, length_ptr)) == NULL)
++		fatal("buffer_get_string_ptr: buffer error");
++	return (ret);
++}
++
+ /*
+  * Stores and arbitrary binary string in the buffer.
+  */
+diff -up openssh-5.3p1/buffer.h.certificates openssh-5.3p1/buffer.h
+--- openssh-5.3p1/buffer.h.certificates	2008-05-19 06:59:37.000000000 +0200
++++ openssh-5.3p1/buffer.h	2013-07-18 15:38:20.432186429 +0200
+@@ -81,6 +81,7 @@ int	buffer_get_short_ret(u_short *, Buff
+ int	buffer_get_int_ret(u_int *, Buffer *);
+ int	buffer_get_int64_ret(u_int64_t *, Buffer *);
+ void	*buffer_get_string_ret(Buffer *, u_int *);
++void   *buffer_get_string_ptr_ret(Buffer *, u_int *);
+ int	buffer_get_char_ret(char *, Buffer *);
+ 
+ #endif				/* BUFFER_H */
+diff -U0 openssh-5.3p1/ChangeLog.certificates openssh-5.3p1/ChangeLog
+--- openssh-5.3p1/ChangeLog.certificates	2013-07-18 15:38:20.433186424 +0200
++++ openssh-5.3p1/ChangeLog	2013-07-18 15:49:51.001176701 +0200
+@@ -9,0 +10,192 @@
++20100521
++ - (djm) OpenBSD CVS Sync
++   - djm at cvs.openbsd.org 2010/05/07 11:31:26
++     [regress/Makefile regress/cert-userkey.sh]
++     regress tests for AuthorizedPrincipalsFile and "principals=" key option.
++     feedback and ok markus@
++   - djm at cvs.openbsd.org 2010/05/11 02:58:04
++     [auth-rsa.c]
++     don't accept certificates marked as "cert-authority" here; ok markus@
++   - djm at cvs.openbsd.org 2010/05/14 00:47:22
++     [ssh-add.c]
++     check that the certificate matches the corresponding private key before
++     grafting it on
++   - djm at cvs.openbsd.org 2010/05/20 11:25:26
++     [auth2-pubkey.c]
++     fix logspam when key options (from="..." especially) deny non-matching
++     keys; reported by henning@ also bz#1765; ok markus@ dtucker@
++   - djm at cvs.openbsd.org 2010/05/20 23:46:02
++     [PROTOCOL.certkeys auth-options.c ssh-keygen.c]
++     Move the permit-* options to the non-critical "extensions" field for v01
++     certificates. The logic is that if another implementation fails to
++     implement them then the connection just loses features rather than fails
++     outright.
++     
++     ok markus@
++
++20100510
++   - djm at cvs.openbsd.org 2010/05/07 11:30:30
++     [auth-options.c auth-options.h auth.c auth.h auth2-pubkey.c]
++     [key.c servconf.c servconf.h sshd.8 sshd_config.5]
++     add some optional indirection to matching of principal names listed
++     in certificates. Currently, a certificate must include the a user's name
++     to be accepted for authentication. This change adds the ability to
++     specify a list of certificate principal names that are acceptable.
++     
++     When authenticating using a CA trusted through ~/.ssh/authorized_keys,
++     this adds a new principals="name1[,name2,...]" key option.
++     
++     For CAs listed through sshd_config's TrustedCAKeys option, a new config
++     option "AuthorizedPrincipalsFile" specifies a per-user file containing
++     the list of acceptable names.
++     
++     If either option is absent, the current behaviour of requiring the
++     username to appear in principals continues to apply.
++     
++     These options are useful for role accounts, disjoint account namespaces
++     and "user at realm"-style naming policies in certificates.
++     
++     feedback and ok markus@
++   - jmc at cvs.openbsd.org 2010/05/07 12:49:17
++     [sshd_config.5]
++     tweak previous;
++20100418
++ - OpenBSD CVS Sync
++   - jmc at cvs.openbsd.org 2010/04/16 06:45:01
++     [ssh_config.5]
++     tweak previous; ok djm
++   - jmc at cvs.openbsd.org 2010/04/16 06:47:04
++     [ssh-keygen.1 ssh-keygen.c]
++     tweak previous; ok djm
++   - djm at cvs.openbsd.org 2010/04/16 01:58:45
++     [regress/cert-hostkey.sh regress/cert-userkey.sh]
++     regression tests for v01 certificate format
++     includes interop tests for v00 certs
++
++20100416
++   - djm at cvs.openbsd.org 2010/04/16 01:47:26
++     [PROTOCOL.certkeys auth-options.c auth-options.h auth-rsa.c]
++     [auth2-pubkey.c authfd.c key.c key.h myproposal.h ssh-add.c]
++     [ssh-agent.c ssh-dss.c ssh-keygen.1 ssh-keygen.c ssh-rsa.c]
++     [sshconnect.c sshconnect2.c sshd.c]
++     revised certificate format ssh-{dss,rsa}-cert-v01 at openssh.com with the
++     following changes:
++     
++     move the nonce field to the beginning of the certificate where it can
++     better protect against chosen-prefix attacks on the signature hash
++     
++     Rename "constraints" field to "critical options"
++     
++     Add a new non-critical "extensions" field
++     
++     Add a serial number
++     
++     The older format is still support for authentication and cert generation
++     (use "ssh-keygen -t v00 -s ca_key ..." to generate a v00 certificate)
++     
++     ok markus@
++
++20100321
++  - djm at cvs.openbsd.org 2010/03/10 23:27:17
++    [auth2-pubkey.c]
++    correct certificate logging and make it more consistent between
++    authorized_keys and TrustedCAKeys; ok markus@
++  - djm at cvs.openbsd.org 2010/03/13 21:45:46
++    [ssh-keygen.1]
++    Certificates are named *-cert.pub, not *_cert.pub; committing a diff
++    from stevesk@ ok me
++  - jmc at cvs.openbsd.org 2010/03/13 23:38:13
++    [ssh-keygen.1]
++    fix a formatting error (args need quoted); noted by stevesk
++  - stevesk at cvs.openbsd.org 2010/03/15 19:40:02
++    [key.c key.h ssh-keygen.c]
++    also print certificate type (user or host) for ssh-keygen -L
++    ok djm kettenis
++  - stevesk at cvs.openbsd.org 2010/03/16 15:46:52
++    [auth-options.c]
++    spelling in error message. ok djm kettenis
++
++20100304
++- OpenBSD CVS Sync
++  - djm at cvs.openbsd.org 2010/03/03 01:44:36
++    [auth-options.c key.c]
++    reject strings with embedded ASCII nul chars in certificate key IDs,
++    principal names and constraints
++  - djm at cvs.openbsd.org 2010/03/04 01:44:57
++    [key.c]
++    use buffer_get_string_ptr_ret() where we are checking the return
++    value explicitly instead of the fatal()-causing buffer_get_string_ptr()
++  - djm at cvs.openbsd.org 2010/03/04 10:36:03
++    [auth-rh-rsa.c auth-rsa.c auth.c auth.h auth2-hostbased.c auth2-pubkey.c]
++    [authfile.c authfile.h hostfile.c hostfile.h servconf.c servconf.h]
++    [ssh-keygen.c ssh.1 sshconnect.c sshd_config.5]
++    Add a TrustedUserCAKeys option to sshd_config to specify CA keys that
++    are trusted to authenticate users (in addition than doing it per-user
++    in authorized_keys).
++    
++    Add a RevokedKeys option to sshd_config and a @revoked marker to
++    known_hosts to allow keys to me revoked and banned for user or host
++    authentication.
++    
++    feedback and ok markus@
++  - djm at cvs.openbsd.org 2010/03/03 00:47:23
++    [regress/cert-hostkey.sh regress/cert-userkey.sh]
++    add an extra test to ensure that authentication with the wrong
++    certificate fails as it should (and it does)
++  - djm at cvs.openbsd.org 2010/03/04 10:38:23
++    [regress/cert-hostkey.sh regress/cert-userkey.sh]
++    additional regression tests for revoked keys and TrustedUserCAKeys
++  - djm at cvs.openbsd.org 2010/03/04 20:35:08
++    [ssh-keygen.1 ssh-keygen.c]
++    Add a -L flag to print the contents of a certificate; ok markus@
++  - djm at cvs.openbsd.org 2010/03/04 23:19:29
++    [ssh.1 sshd.8]
++    move section on CA and revoked keys from ssh.1 to sshd.8's known hosts
++    format section and rework it a bit; requested by jmc@
++  - jmc at cvs.openbsd.org 2010/03/05 06:50:35
++    [ssh.1 sshd.8]
++    tweak previous;
++  - jmc at cvs.openbsd.org 2010/03/05 08:31:20
++    [ssh.1]
++    document certificate authentication; help/ok djm
++  - djm at cvs.openbsd.org 2010/03/05 10:28:21
++    [ssh-add.1 ssh.1 ssh_config.5]
++    mention loading of certificate files from [private]-cert.pub when
++    they are present; feedback and ok jmc@
++
++20100226
++- OpenBSD CVS Sync
++  - djm at cvs.openbsd.org 2010/02/26 20:29:54
++    [PROTOCOL PROTOCOL.agent PROTOCOL.certkeys addrmatch.c auth-options.c]
++    [auth-options.h auth.h auth2-pubkey.c authfd.c dns.c dns.h hostfile.c]
++    [hostfile.h kex.h kexdhs.c kexgexs.c key.c key.h match.h monitor.c]
++    [myproposal.h servconf.c servconf.h ssh-add.c ssh-agent.c ssh-dss.c]
++    [ssh-keygen.1 ssh-keygen.c ssh-rsa.c ssh.1 ssh.c ssh2.h sshconnect.c]
++    [sshconnect2.c sshd.8 sshd.c sshd_config.5]
++    Add support for certificate key types for users and hosts.
++    
++    OpenSSH certificate key types are not X.509 certificates, but a much
++    simpler format that encodes a public key, identity information and
++    some validity constraints and signs it with a CA key. CA keys are
++    regular SSH keys. This certificate style avoids the attack surface
++    of X.509 certificates and is very easy to deploy.
++    
++    Certified host keys allow automatic acceptance of new host keys
++    when a CA certificate is marked as trusted in ~/.ssh/known_hosts.
++    see VERIFYING HOST KEYS in ssh(1) for details.
++    
++    Certified user keys allow authentication of users when the signing
++    CA key is marked as trusted in authorized_keys. See "AUTHORIZED_KEYS
++    FILE FORMAT" in sshd(8) for details.
++    
++    Certificates are minted using ssh-keygen(1), documentation is in
++    the "CERTIFICATES" section of that manpage.
++    
++    Documentation on the format of certificates is in the file
++    PROTOCOL.certkeys
++    
++    feedback and ok markus@
++  - djm at cvs.openbsd.org 2010/02/26 20:33:21
++    [Makefile regress/cert-hostkey.sh regress/cert-userkey.sh]
++    regression tests for certified keys
++
+diff -up openssh-5.3p1/channels.c.netcat.certificates openssh-5.3p1/channels.c.netcat
+diff -up openssh-5.3p1/channels.h.netcat.certificates openssh-5.3p1/channels.h.netcat
+diff -up openssh-5.3p1/dns.c.certificates openssh-5.3p1/dns.c
+--- openssh-5.3p1/dns.c.certificates	2013-07-18 15:38:20.299187008 +0200
++++ openssh-5.3p1/dns.c	2013-07-18 15:38:20.435186416 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: dns.c,v 1.25 2008/06/12 00:03:49 dtucker Exp $ */
++/* $OpenBSD: dns.c,v 1.26 2010/02/26 20:29:54 djm Exp $ */
+ 
+ /*
+  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
+@@ -75,7 +75,7 @@ dns_result_totext(unsigned int res)
+  */
+ static int
+ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
+-    u_char **digest, u_int *digest_len, const Key *key)
++    u_char **digest, u_int *digest_len, Key *key)
+ {
+ 	int success = 0;
+ 
+@@ -172,7 +172,7 @@ is_numeric_hostname(const char *hostname
+  */
+ int
+ verify_host_key_dns(const char *hostname, struct sockaddr *address,
+-    const Key *hostkey, int *flags)
++    Key *hostkey, int *flags)
+ {
+ 	u_int counter;
+ 	int result;
+@@ -283,7 +283,7 @@ verify_host_key_dns(const char *hostname
+  * Export the fingerprint of a key as a DNS resource record
+  */
+ int
+-export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic)
++export_dns_rr(const char *hostname, Key *key, FILE *f, int generic)
+ {
+ 	u_int8_t rdata_pubkey_algorithm = 0;
+ 	u_int8_t rdata_digest_type = SSHFP_HASH_SHA1;
+diff -up openssh-5.3p1/dns.h.certificates openssh-5.3p1/dns.h
+--- openssh-5.3p1/dns.h.certificates	2006-08-05 04:39:40.000000000 +0200
++++ openssh-5.3p1/dns.h	2013-07-18 15:38:20.435186416 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: dns.h,v 1.10 2006/08/03 03:34:42 deraadt Exp $ */
++/* $OpenBSD: dns.h,v 1.11 2010/02/26 20:29:54 djm Exp $ */
+ 
+ /*
+  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
+@@ -46,7 +46,7 @@ enum sshfp_hashes {
+ #define DNS_VERIFY_MATCH	0x00000002
+ #define DNS_VERIFY_SECURE	0x00000004
+ 
+-int	verify_host_key_dns(const char *, struct sockaddr *, const Key *, int *);
+-int	export_dns_rr(const char *, const Key *, FILE *, int);
++int	verify_host_key_dns(const char *, struct sockaddr *, Key *, int *);
++int	export_dns_rr(const char *, Key *, FILE *, int);
+ 
+ #endif /* DNS_H */
+diff -up openssh-5.3p1/hostfile.c.certificates openssh-5.3p1/hostfile.c
+--- openssh-5.3p1/hostfile.c.certificates	2006-09-01 07:38:36.000000000 +0200
++++ openssh-5.3p1/hostfile.c	2013-07-18 15:38:20.435186416 +0200
+@@ -183,6 +183,41 @@ hostfile_check_key(int bits, const Key *
+ 	return 1;
+ }
+ 
++static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA }
++check_markers(char **cpp)
++{
++	char marker[32], *sp, *cp = *cpp;
++	int ret = MRK_NONE;
++
++	while (*cp == '@') {
++		/* Only one marker is allowed */
++		if (ret != MRK_NONE)
++			return MRK_ERROR;
++		/* Markers are terminated by whitespace */
++		if ((sp = strchr(cp, ' ')) == NULL &&
++		    (sp = strchr(cp, '\t')) == NULL)
++			return MRK_ERROR;
++		/* Extract marker for comparison */
++		if (sp <= cp + 1 || sp >= cp + sizeof(marker))
++			return MRK_ERROR;
++		memcpy(marker, cp, sp - cp);
++		marker[sp - cp] = '\0';
++		if (strcmp(marker, CA_MARKER) == 0)
++			ret = MRK_CA;
++		else if (strcmp(marker, REVOKE_MARKER) == 0)
++			ret = MRK_REVOKE;
++		else
++			return MRK_ERROR;
++
++		/* Skip past marker and any whitespace that follows it */
++		cp = sp;
++		for (; *cp == ' ' || *cp == '\t'; cp++)
++			;
++	}
++	*cpp = cp;
++	return ret;
++}
++
+ /*
+  * Checks whether the given host (which must be in all lowercase) is already
+  * in the list of our known hosts. Returns HOST_OK if the host is known and
+@@ -195,16 +230,20 @@ hostfile_check_key(int bits, const Key *
+ 
+ static HostStatus
+ check_host_in_hostfile_by_key_or_type(const char *filename,
+-    const char *host, const Key *key, int keytype, Key *found, int *numret)
++    const char *host, const Key *key, int keytype, Key *found,
++    int want_revocation, int *numret)
+ {
+ 	FILE *f;
+ 	char line[8192];
+-	int linenum = 0;
++	int want, have, linenum = 0, want_cert = key_is_cert(key);
+ 	u_int kbits;
+ 	char *cp, *cp2, *hashed_host;
+ 	HostStatus end_return;
+ 
+-	debug3("check_host_in_hostfile: filename %s", filename);
++	debug3("check_host_in_hostfile: host %s filename %s", host, filename);
++
++	if (want_revocation && (key == NULL || keytype != 0 || found != NULL))
++		fatal("%s: invalid arguments", __func__);
+ 
+ 	/* Open the file containing the list of known hosts. */
+ 	f = fopen(filename, "r");
+@@ -229,6 +268,20 @@ check_host_in_hostfile_by_key_or_type(co
+ 		if (!*cp || *cp == '#' || *cp == '\n')
+ 			continue;
+ 
++		if (want_revocation)
++			want = MRK_REVOKE;
++		else if (want_cert)
++			want = MRK_CA;
++		else
++			want = MRK_NONE;
++
++		if ((have = check_markers(&cp)) == MRK_ERROR) {
++			verbose("%s: invalid marker at %s:%d",
++			    __func__, filename, linenum);
++			continue;
++		} else if (want != have)
++			continue;
++
+ 		/* Find the end of the host name portion. */
+ 		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
+ 			;
+@@ -250,6 +303,9 @@ check_host_in_hostfile_by_key_or_type(co
+ 		/* Got a match.  Skip host name. */
+ 		cp = cp2;
+ 
++		if (want_revocation)
++			found = key_new(KEY_UNSPEC);
++
+ 		/*
+ 		 * Extract the key from the line.  This will skip any leading
+ 		 * whitespace.  Ignore badly formatted lines.
+@@ -272,9 +328,35 @@ check_host_in_hostfile_by_key_or_type(co
+ 		if (!hostfile_check_key(kbits, found, host, filename, linenum))
+ 			continue;
+ 
++		if (want_revocation) {
++			if (key_is_cert(key) &&
++			    key_equal_public(key->cert->signature_key, found)) {
++				verbose("check_host_in_hostfile: revoked CA "
++				    "line %d", linenum);
++				key_free(found);
++				fclose(f);
++				return HOST_REVOKED;
++			}
++			if (key_equal_public(key, found)) {
++				verbose("check_host_in_hostfile: revoked key "
++				    "line %d", linenum);
++				key_free(found);
++				fclose(f);
++				return HOST_REVOKED;
++			}
++			key_free(found);
++			continue;
++		}
++
+ 		/* Check if the current key is the same as the given key. */
+-		if (key_equal(key, found)) {
+-			/* Ok, they match. */
++		if (want_cert && key_equal(key->cert->signature_key, found)) {
++			/* Found CA cert for key */
++			debug3("check_host_in_hostfile: CA match line %d",
++			    linenum);
++			fclose(f);
++			return HOST_OK;
++		} else if (!want_cert && key_equal(key, found)) {
++			/* Found identical key */
+ 			debug3("check_host_in_hostfile: match line %d", linenum);
+ 			fclose(f);
+ 			return HOST_OK;
+@@ -302,8 +384,11 @@ check_host_in_hostfile(const char *filen
+ {
+ 	if (key == NULL)
+ 		fatal("no key to look up");
+-	return (check_host_in_hostfile_by_key_or_type(filename, host, key, 0,
+-	    found, numret));
++	if (check_host_in_hostfile_by_key_or_type(filename, host,
++	    key, 0, NULL, 1, NULL) == HOST_REVOKED)
++		return HOST_REVOKED;
++	return check_host_in_hostfile_by_key_or_type(filename, host, key, 0,
++	    found, 0, numret);
+ }
+ 
+ int
+@@ -311,7 +396,7 @@ lookup_key_in_hostfile_by_type(const cha
+     int keytype, Key *found, int *numret)
+ {
+ 	return (check_host_in_hostfile_by_key_or_type(filename, host, NULL,
+-	    keytype, found, numret) == HOST_FOUND);
++	    keytype, found, 0, numret) == HOST_FOUND);
+ }
+ 
+ /*
+diff -up openssh-5.3p1/hostfile.h.certificates openssh-5.3p1/hostfile.h
+--- openssh-5.3p1/hostfile.h.certificates	2006-03-26 05:30:01.000000000 +0200
++++ openssh-5.3p1/hostfile.h	2013-07-18 15:38:20.436186411 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: hostfile.h,v 1.16 2006/03/25 22:22:43 djm Exp $ */
++/* $OpenBSD: hostfile.h,v 1.18 2010/03/04 10:36:03 djm Exp $ */
+ 
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+@@ -15,7 +15,7 @@
+ #define HOSTFILE_H
+ 
+ typedef enum {
+-	HOST_OK, HOST_NEW, HOST_CHANGED, HOST_FOUND
++	HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND
+ }       HostStatus;
+ 
+ int	 hostfile_read_key(char **, u_int *, Key *);
+@@ -28,6 +28,9 @@ int	lookup_key_in_hostfile_by_type(const
+ #define HASH_MAGIC	"|1|"
+ #define HASH_DELIM	'|'
+ 
++#define CA_MARKER	"@cert-authority"
++#define REVOKE_MARKER	"@revoked"
++
+ char	*host_hash(const char *, const char *, u_int);
+ 
+ #endif
+diff -up openssh-5.3p1/kexdhs.c.certificates openssh-5.3p1/kexdhs.c
+--- openssh-5.3p1/kexdhs.c.certificates	2009-06-21 11:00:20.000000000 +0200
++++ openssh-5.3p1/kexdhs.c	2013-07-18 15:38:20.436186411 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: kexdhs.c,v 1.10 2009/06/21 07:37:15 dtucker Exp $ */
++/* $OpenBSD: kexdhs.c,v 1.11 2010/02/26 20:29:54 djm Exp $ */
+ /*
+  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+  *
+@@ -50,7 +50,7 @@ kexdh_server(Kex *kex)
+ {
+ 	BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
+ 	DH *dh;
+-	Key *server_host_key;
++	Key *server_host_public, *server_host_private;
+ 	u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
+ 	u_int sbloblen, klen, hashlen, slen;
+ 	int kout;
+@@ -71,11 +71,16 @@ kexdh_server(Kex *kex)
+ 	debug("expecting SSH2_MSG_KEXDH_INIT");
+ 	packet_read_expect(SSH2_MSG_KEXDH_INIT);
+ 
+-	if (kex->load_host_key == NULL)
++	if (kex->load_host_public_key == NULL ||
++	    kex->load_host_private_key == NULL)
+ 		fatal("Cannot load hostkey");
+-	server_host_key = kex->load_host_key(kex->hostkey_type);
+-	if (server_host_key == NULL)
++	server_host_public = kex->load_host_public_key(kex->hostkey_type);
++	if (server_host_public == NULL)
+ 		fatal("Unsupported hostkey type %d", kex->hostkey_type);
++	server_host_private = kex->load_host_private_key(kex->hostkey_type);
++	if (server_host_private == NULL)
++		fatal("Missing private key for hostkey type %d",
++		    kex->hostkey_type);
+ 
+ 	/* key, cert */
+ 	if ((dh_client_pub = BN_new()) == NULL)
+@@ -113,7 +118,7 @@ kexdh_server(Kex *kex)
+ 	memset(kbuf, 0, klen);
+ 	xfree(kbuf);
+ 
+-	key_to_blob(server_host_key, &server_host_key_blob, &sbloblen);
++	key_to_blob(server_host_public, &server_host_key_blob, &sbloblen);
+ 
+ 	/* calc H */
+ 	kex_dh_hash(
+@@ -137,7 +142,7 @@ kexdh_server(Kex *kex)
+ 	}
+ 
+ 	/* sign H */
+-	if (PRIVSEP(key_sign(server_host_key, &signature, &slen, hash,
++	if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash,
+ 	    hashlen)) < 0)
+ 		fatal("kexdh_server: key_sign failed");
+ 
+diff -up openssh-5.3p1/kexgexs.c.certificates openssh-5.3p1/kexgexs.c
+--- openssh-5.3p1/kexgexs.c.certificates	2009-06-21 11:00:20.000000000 +0200
++++ openssh-5.3p1/kexgexs.c	2013-07-18 15:38:20.436186411 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: kexgexs.c,v 1.12 2009/06/21 07:37:15 dtucker Exp $ */
++/* $OpenBSD: kexgexs.c,v 1.13 2010/02/26 20:29:54 djm Exp $ */
+ /*
+  * Copyright (c) 2000 Niels Provos.  All rights reserved.
+  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+@@ -52,18 +52,24 @@ void
+ kexgex_server(Kex *kex)
+ {
+ 	BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
+-	Key *server_host_key;
++	Key *server_host_public, *server_host_private;
+ 	DH *dh;
+ 	u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
+ 	u_int sbloblen, klen, slen, hashlen;
+ 	int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1;
+ 	int type, kout;
+ 
+-	if (kex->load_host_key == NULL)
++	if (kex->load_host_public_key == NULL ||
++	    kex->load_host_private_key == NULL)
+ 		fatal("Cannot load hostkey");
+-	server_host_key = kex->load_host_key(kex->hostkey_type);
+-	if (server_host_key == NULL)
++	server_host_public = kex->load_host_public_key(kex->hostkey_type);
++	if (server_host_public == NULL)
+ 		fatal("Unsupported hostkey type %d", kex->hostkey_type);
++	server_host_private = kex->load_host_private_key(kex->hostkey_type);
++	if (server_host_private == NULL)
++		fatal("Missing private key for hostkey type %d",
++		    kex->hostkey_type);
++
+ 
+ 	type = packet_read();
+ 	switch (type) {
+@@ -149,7 +155,7 @@ kexgex_server(Kex *kex)
+ 	memset(kbuf, 0, klen);
+ 	xfree(kbuf);
+ 
+-	key_to_blob(server_host_key, &server_host_key_blob, &sbloblen);
++	key_to_blob(server_host_public, &server_host_key_blob, &sbloblen);
+ 
+ 	if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD)
+ 		omin = min = omax = max = -1;
+@@ -179,7 +185,7 @@ kexgex_server(Kex *kex)
+ 	}
+ 
+ 	/* sign H */
+-	if (PRIVSEP(key_sign(server_host_key, &signature, &slen, hash,
++	if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash,
+ 	    hashlen)) < 0)
+ 		fatal("kexgex_server: key_sign failed");
+ 
+diff -up openssh-5.3p1/kex.h.certificates openssh-5.3p1/kex.h
+--- openssh-5.3p1/kex.h.certificates	2013-07-18 15:38:20.416186499 +0200
++++ openssh-5.3p1/kex.h	2013-07-18 15:38:20.436186411 +0200
+@@ -133,7 +133,8 @@ struct Kex {
+ 	char	*client_version_string;
+ 	char	*server_version_string;
+ 	int	(*verify_host_key)(Key *);
+-	Key	*(*load_host_key)(int);
++	Key	*(*load_host_public_key)(int);
++	Key	*(*load_host_private_key)(int);
+ 	int	(*host_key_index)(Key *);
+ 	void	(*kex[KEX_MAX])(Kex *);
+ };
+diff -up openssh-5.3p1/key.c.certificates openssh-5.3p1/key.c
+--- openssh-5.3p1/key.c.certificates	2013-07-18 15:38:20.334186856 +0200
++++ openssh-5.3p1/key.c	2013-07-18 15:38:20.437186407 +0200
+@@ -52,6 +52,22 @@
+ #include "uuencode.h"
+ #include "buffer.h"
+ #include "log.h"
++#include "ssh2.h"
++
++static struct KeyCert *
++cert_new(void)
++{
++	struct KeyCert *cert;
++
++	cert = xcalloc(1, sizeof(*cert));
++	buffer_init(&cert->certblob);
++	buffer_init(&cert->critical);
++	buffer_init(&cert->extensions);
++	cert->key_id = NULL;
++	cert->principals = NULL;
++	cert->signature_key = NULL;
++	return cert;
++}
+ 
+ Key *
+ key_new(int type)
+@@ -63,9 +79,12 @@ key_new(int type)
+ 	k->type = type;
+ 	k->dsa = NULL;
+ 	k->rsa = NULL;
++	k->cert = NULL;
+ 	switch (k->type) {
+ 	case KEY_RSA1:
+ 	case KEY_RSA:
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 		if ((rsa = RSA_new()) == NULL)
+ 			fatal("key_new: RSA_new failed");
+ 		if ((rsa->n = BN_new()) == NULL)
+@@ -75,6 +94,8 @@ key_new(int type)
+ 		k->rsa = rsa;
+ 		break;
+ 	case KEY_DSA:
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
+ 		if ((dsa = DSA_new()) == NULL)
+ 			fatal("key_new: DSA_new failed");
+ 		if ((dsa->p = BN_new()) == NULL)
+@@ -93,6 +114,10 @@ key_new(int type)
+ 		fatal("key_new: bad key type %d", k->type);
+ 		break;
+ 	}
++
++	if (key_is_cert(k))
++		k->cert = cert_new();
++
+ 	return k;
+ }
+ 
+@@ -144,13 +169,14 @@ key_new_nss_copy(int type, const Key *c)
+ #endif
+ 
+ 
+-Key *
+-key_new_private(int type)
++void
++key_add_private(Key *k)
+ {
+-	Key *k = key_new(type);
+ 	switch (k->type) {
+ 	case KEY_RSA1:
+ 	case KEY_RSA:
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 		if ((k->rsa->d = BN_new()) == NULL)
+ 			fatal("key_new_private: BN_new failed");
+ 		if ((k->rsa->iqmp = BN_new()) == NULL)
+@@ -165,6 +191,8 @@ key_new_private(int type)
+ 			fatal("key_new_private: BN_new failed");
+ 		break;
+ 	case KEY_DSA:
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
+ 		if ((k->dsa->priv_key = BN_new()) == NULL)
+ 			fatal("key_new_private: BN_new failed");
+ 		break;
+@@ -173,9 +201,35 @@ key_new_private(int type)
+ 	default:
+ 		break;
+ 	}
++}
++
++Key *
++key_new_private(int type)
++{
++	Key *k = key_new(type);
++
++	key_add_private(k);
+ 	return k;
+ }
+ 
++static void
++cert_free(struct KeyCert *cert)
++{
++	u_int i;
++
++	buffer_free(&cert->certblob);
++	buffer_free(&cert->critical);
++	buffer_free(&cert->extensions);
++	if (cert->key_id != NULL)
++		xfree(cert->key_id);
++	for (i = 0; i < cert->nprincipals; i++)
++		xfree(cert->principals[i]);
++	if (cert->principals != NULL)
++		xfree(cert->principals);
++	if (cert->signature_key != NULL)
++		key_free(cert->signature_key);
++}
++
+ void
+ key_free(Key *k)
+ {
+@@ -184,11 +238,15 @@ key_free(Key *k)
+ 	switch (k->type) {
+ 	case KEY_RSA1:
+ 	case KEY_RSA:
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 		if (k->rsa != NULL)
+ 			RSA_free(k->rsa);
+ 		k->rsa = NULL;
+ 		break;
+ 	case KEY_DSA:
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
+ 		if (k->dsa != NULL)
+ 			DSA_free(k->dsa);
+ 		k->dsa = NULL;
+@@ -212,20 +270,51 @@ key_free(Key *k)
+ 		xfree(k->nss);
+ 	}
+ #endif
++	if (key_is_cert(k)) {
++		if (k->cert != NULL)
++			cert_free(k->cert);
++		k->cert = NULL;
++	}
++
+ 	xfree(k);
+ }
+ 
++static int
++cert_compare(struct KeyCert *a, struct KeyCert *b)
++{
++	if (a == NULL && b == NULL)
++		return 1;
++	if (a == NULL || b == NULL)
++		return 0;
++	if (buffer_len(&a->certblob) != buffer_len(&b->certblob))
++		return 0;
++	if (memcmp(buffer_ptr(&a->certblob), buffer_ptr(&b->certblob),
++	    buffer_len(&a->certblob)) != 0)
++		return 0;
++	return 1;
++}
++
++/*
++ * Compare public portions of key only, allowing comparisons between
++ * certificates and plain keys too.
++ */
+ int
+-key_equal(const Key *a, const Key *b)
++key_equal_public(const Key *a, const Key *b)
+ {
+-	if (a == NULL || b == NULL || a->type != b->type)
++	if (a == NULL || b == NULL ||
++	    key_type_plain(a->type) != key_type_plain(b->type))
+ 		return 0;
++
+ 	switch (a->type) {
+ 	case KEY_RSA1:
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 	case KEY_RSA:
+ 		return a->rsa != NULL && b->rsa != NULL &&
+ 		    BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
+ 		    BN_cmp(a->rsa->n, b->rsa->n) == 0;
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
+ 	case KEY_DSA:
+ 		return a->dsa != NULL && b->dsa != NULL &&
+ 		    BN_cmp(a->dsa->p, b->dsa->p) == 0 &&
+@@ -238,16 +327,27 @@ key_equal(const Key *a, const Key *b)
+ 	/* NOTREACHED */
+ }
+ 
++int
++key_equal(const Key *a, const Key *b)
++{
++	if (a == NULL || b == NULL || a->type != b->type)
++		return 0;
++	if (key_is_cert(a)) {
++		if (!cert_compare(a->cert, b->cert))
++			return 0;
++	}
++	return key_equal_public(a, b);
++}
++
+ u_char*
+-key_fingerprint_raw(const Key *k, enum fp_type dgst_type,
+-    u_int *dgst_raw_length)
++key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length)
+ {
+ 	const EVP_MD *md = NULL;
+ 	EVP_MD_CTX ctx;
+ 	u_char *blob = NULL;
+ 	u_char *retval = NULL;
+ 	u_int len = 0;
+-	int nlen, elen;
++	int nlen, elen, otype;
+ 
+ 	*dgst_raw_length = 0;
+ 
+@@ -275,6 +375,16 @@ key_fingerprint_raw(const Key *k, enum f
+ 	case KEY_RSA:
+ 		key_to_blob(k, &blob, &len);
+ 		break;
++	case KEY_DSA_CERT_V00:
++	case KEY_RSA_CERT_V00:
++	case KEY_DSA_CERT:
++	case KEY_RSA_CERT:
++		/* We want a fingerprint of the _key_ not of the cert */
++		otype = k->type;
++		k->type = key_type_plain(k->type);
++		key_to_blob(k, &blob, &len);
++		k->type = otype;
++		break;
+ 	case KEY_UNSPEC:
+ 		return retval;
+ 	default:
+@@ -469,7 +579,7 @@ key_fingerprint_randomart(u_char *dgst_r
+ }
+ 
+ char *
+-key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
++key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
+ {
+ 	char *retval = NULL;
+ 	u_char *dgst_raw;
+@@ -588,6 +698,10 @@ key_read(Key *ret, char **cpp)
+ 	case KEY_UNSPEC:
+ 	case KEY_RSA:
+ 	case KEY_DSA:
++	case KEY_DSA_CERT_V00:
++	case KEY_RSA_CERT_V00:
++	case KEY_DSA_CERT:
++	case KEY_RSA_CERT:
+ 		space = strchr(cp, ' ');
+ 		if (space == NULL) {
+ 			debug3("key_read: missing whitespace");
+@@ -632,25 +746,36 @@ key_read(Key *ret, char **cpp)
+ 			return -1;
+ 		}
+ /*XXXX*/
+-		if (ret->type == KEY_RSA) {
++		if (key_is_cert(ret)) {
++			if (!key_is_cert(k)) {
++				error("key_read: loaded key is not a cert");
++				key_free(k);
++				return -1;
++			}
++			if (ret->cert != NULL)
++				cert_free(ret->cert);
++			ret->cert = k->cert;
++			k->cert = NULL;
++		}
++		if (key_type_plain(ret->type) == KEY_RSA) {
+ 			if (ret->rsa != NULL)
+ 				RSA_free(ret->rsa);
+ 			ret->rsa = k->rsa;
+ 			k->rsa = NULL;
+-			success = 1;
+ #ifdef DEBUG_PK
+ 			RSA_print_fp(stderr, ret->rsa, 8);
+ #endif
+-		} else {
++		}
++		if (key_type_plain(ret->type) == KEY_DSA) {
+ 			if (ret->dsa != NULL)
+ 				DSA_free(ret->dsa);
+ 			ret->dsa = k->dsa;
+ 			k->dsa = NULL;
+-			success = 1;
+ #ifdef DEBUG_PK
+ 			DSA_print_fp(stderr, ret->dsa, 8);
+ #endif
+ 		}
++		success = 1;
+ /*XXXX*/
+ 		key_free(k);
+ 		if (success != 1)
+@@ -677,28 +802,55 @@ key_write(const Key *key, FILE *f)
+ 	u_char *blob;
+ 	char *uu;
+ 
+-	if (key->type == KEY_RSA1 && key->rsa != NULL) {
++	if (key_is_cert(key)) {
++		if (key->cert == NULL) {
++			error("%s: no cert data", __func__);
++			return 0;
++		}
++		if (buffer_len(&key->cert->certblob) == 0) {
++			error("%s: no signed certificate blob", __func__);
++			return 0;
++		}
++	}
++
++	switch (key->type) {
++	case KEY_RSA1:
++		if (key->rsa == NULL)
++			return 0;
+ 		/* size of modulus 'n' */
+ 		bits = BN_num_bits(key->rsa->n);
+ 		fprintf(f, "%u", bits);
+ 		if (write_bignum(f, key->rsa->e) &&
+-		    write_bignum(f, key->rsa->n)) {
+-			success = 1;
+-		} else {
+-			error("key_write: failed for RSA key");
+-		}
+-	} else if ((key->type == KEY_DSA && key->dsa != NULL) ||
+-	    (key->type == KEY_RSA && key->rsa != NULL)) {
+-		key_to_blob(key, &blob, &len);
+-		uu = xmalloc(2*len);
+-		n = uuencode(blob, len, uu, 2*len);
+-		if (n > 0) {
+-			fprintf(f, "%s %s", key_ssh_name(key), uu);
+-			success = 1;
+-		}
+-		xfree(blob);
+-		xfree(uu);
++		    write_bignum(f, key->rsa->n))
++			return 1;
++		error("key_write: failed for RSA key");
++		return 0;
++	case KEY_DSA:
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
++		if (key->dsa == NULL)
++			return 0;
++		break;
++	case KEY_RSA:
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
++		if (key->rsa == NULL)
++			return 0;
++		break;
++	default:
++		return 0;
+ 	}
++
++	key_to_blob(key, &blob, &len);
++	uu = xmalloc(2*len);
++	n = uuencode(blob, len, uu, 2*len);
++	if (n > 0) {
++		fprintf(f, "%s %s", key_ssh_name(key), uu);
++		success = 1;
++	}
++	xfree(blob);
++	xfree(uu);
++
+ 	return success;
+ }
+ 
+@@ -712,11 +864,32 @@ key_type(const Key *k)
+ 		return "RSA";
+ 	case KEY_DSA:
+ 		return "DSA";
++	case KEY_RSA_CERT_V00:
++		return "RSA-CERT-V00";
++	case KEY_DSA_CERT_V00:
++		return "DSA-CERT-V00";
++	case KEY_RSA_CERT:
++		return "RSA-CERT";
++	case KEY_DSA_CERT:
++		return "DSA-CERT";
+ 	}
+ 	return "unknown";
+ }
+ 
+ const char *
++key_cert_type(const Key *k)
++{
++	switch (k->cert->type) {
++	case SSH2_CERT_TYPE_USER:
++		return "user";
++	case SSH2_CERT_TYPE_HOST:
++		return "host";
++	default:
++		return "unknown";
++	}
++}
++
++const char *
+ key_ssh_name(const Key *k)
+ {
+ 	switch (k->type) {
+@@ -724,6 +897,14 @@ key_ssh_name(const Key *k)
+ 		return "ssh-rsa";
+ 	case KEY_DSA:
+ 		return "ssh-dss";
++	case KEY_RSA_CERT_V00:
++		return "ssh-rsa-cert-v00 at openssh.com";
++	case KEY_DSA_CERT_V00:
++		return "ssh-dss-cert-v00 at openssh.com";
++	case KEY_RSA_CERT:
++		return "ssh-rsa-cert-v01 at openssh.com";
++	case KEY_DSA_CERT:
++		return "ssh-dss-cert-v01 at openssh.com";
+ 	}
+ 	return "ssh-unknown";
+ }
+@@ -734,8 +915,12 @@ key_size(const Key *k)
+ 	switch (k->type) {
+ 	case KEY_RSA1:
+ 	case KEY_RSA:
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 		return BN_num_bits(k->rsa->n);
+ 	case KEY_DSA:
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
+ 		return BN_num_bits(k->dsa->p);
+ 	}
+ 	return 0;
+@@ -778,6 +963,11 @@ key_generate(int type, u_int bits)
+ 	case KEY_RSA1:
+ 		k->rsa = rsa_generate_private_key(bits);
+ 		break;
++	case KEY_RSA_CERT_V00:
++	case KEY_DSA_CERT_V00:
++	case KEY_RSA_CERT:
++	case KEY_DSA_CERT:
++		fatal("key_generate: cert keys cannot be generated directly");
+ 	default:
+ 		fatal("key_generate: unknown type %d", type);
+ 	}
+@@ -785,12 +975,59 @@ key_generate(int type, u_int bits)
+ 	return k;
+ }
+ 
++void
++key_cert_copy(const Key *from_key, struct Key *to_key)
++{
++	u_int i;
++	const struct KeyCert *from;
++	struct KeyCert *to;
++
++	if (to_key->cert != NULL) {
++		cert_free(to_key->cert);
++		to_key->cert = NULL;
++	}
++
++	if ((from = from_key->cert) == NULL)
++		return;
++
++	to = to_key->cert = cert_new();
++
++	buffer_append(&to->certblob, buffer_ptr(&from->certblob),
++	    buffer_len(&from->certblob));
++
++	buffer_append(&to->critical,
++	    buffer_ptr(&from->critical), buffer_len(&from->critical));
++	buffer_append(&to->extensions,
++	    buffer_ptr(&from->extensions), buffer_len(&from->extensions));
++
++	to->serial = from->serial;
++	to->type = from->type;
++	to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id);
++	to->valid_after = from->valid_after;
++	to->valid_before = from->valid_before;
++	to->signature_key = from->signature_key == NULL ?
++	    NULL : key_from_private(from->signature_key);
++
++	to->nprincipals = from->nprincipals;
++	if (to->nprincipals > CERT_MAX_PRINCIPALS)
++		fatal("%s: nprincipals (%u) > CERT_MAX_PRINCIPALS (%u)",
++		    __func__, to->nprincipals, CERT_MAX_PRINCIPALS);
++	if (to->nprincipals > 0) {
++		to->principals = xcalloc(from->nprincipals,
++		    sizeof(*to->principals));
++		for (i = 0; i < to->nprincipals; i++)
++			to->principals[i] = xstrdup(from->principals[i]);
++	}
++}
++
+ Key *
+ key_from_private(const Key *k)
+ {
+ 	Key *n = NULL;
+ 	switch (k->type) {
+ 	case KEY_DSA:
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
+ 		n = key_new(k->type);
+ 		if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) ||
+ 		    (BN_copy(n->dsa->q, k->dsa->q) == NULL) ||
+@@ -800,6 +1037,8 @@ key_from_private(const Key *k)
+ 		break;
+ 	case KEY_RSA:
+ 	case KEY_RSA1:
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 		n = key_new(k->type);
+ 		if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) ||
+ 		    (BN_copy(n->rsa->e, k->rsa->e) == NULL))
+@@ -809,6 +1048,8 @@ key_from_private(const Key *k)
+ 		fatal("key_from_private: unknown type %d", k->type);
+ 		break;
+ 	}
++	if (key_is_cert(k))
++		key_cert_copy(k, n);
+ 	return n;
+ }
+ 
+@@ -825,6 +1066,14 @@ key_type_from_name(char *name)
+ 		return KEY_RSA;
+ 	} else if (strcmp(name, "ssh-dss") == 0) {
+ 		return KEY_DSA;
++	} else if (strcmp(name, "ssh-rsa-cert-v00 at openssh.com") == 0) {
++		return KEY_RSA_CERT_V00;
++	} else if (strcmp(name, "ssh-dss-cert-v00 at openssh.com") == 0) {
++		return KEY_DSA_CERT_V00;
++	} else if (strcmp(name, "ssh-rsa-cert-v01 at openssh.com") == 0) {
++		return KEY_RSA_CERT;
++	} else if (strcmp(name, "ssh-dss-cert-v01 at openssh.com") == 0) {
++		return KEY_DSA_CERT;
+ 	} else if (strcmp(name, "null") == 0) {
+ 		return KEY_NULL;
+ 	}
+@@ -854,6 +1103,146 @@ key_names_valid2(const char *names)
+ 	return 1;
+ }
+ 
++static int
++cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen)
++{
++	u_char *principals, *critical, *exts, *sig_key, *sig;
++	u_int signed_len, plen, clen, sklen, slen, kidlen, elen;
++	Buffer tmp;
++	char *principal;
++	int ret = -1;
++	int v00 = key->type == KEY_DSA_CERT_V00 ||
++	    key->type == KEY_RSA_CERT_V00;
++
++	buffer_init(&tmp);
++
++	/* Copy the entire key blob for verification and later serialisation */
++	buffer_append(&key->cert->certblob, blob, blen);
++
++	elen = 0; /* Not touched for v00 certs */
++	principals = exts = critical = sig_key = sig = NULL;
++	if ((!v00 && buffer_get_int64_ret(&key->cert->serial, b) != 0) ||
++	    buffer_get_int_ret(&key->cert->type, b) != 0 ||
++	    (key->cert->key_id = buffer_get_string_ret(b, &kidlen)) == NULL ||
++	    (principals = buffer_get_string_ret(b, &plen)) == NULL ||
++	    buffer_get_int64_ret(&key->cert->valid_after, b) != 0 ||
++	    buffer_get_int64_ret(&key->cert->valid_before, b) != 0 ||
++	    (critical = buffer_get_string_ret(b, &clen)) == NULL ||
++	    (!v00 && (exts = buffer_get_string_ret(b, &elen)) == NULL) ||
++	    (v00 && buffer_get_string_ptr_ret(b, NULL) == NULL) || /* nonce */
++	    buffer_get_string_ptr_ret(b, NULL) == NULL || /* reserved */
++	    (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) {
++		error("%s: parse error", __func__);
++		goto out;
++	}
++
++	if (kidlen != strlen(key->cert->key_id)) {
++		error("%s: key ID contains \\0 character", __func__);
++		goto out;
++	}
++
++	/* Signature is left in the buffer so we can calculate this length */
++	signed_len = buffer_len(&key->cert->certblob) - buffer_len(b);
++
++	if ((sig = buffer_get_string_ret(b, &slen)) == NULL) {
++		error("%s: parse error", __func__);
++		goto out;
++	}
++
++	if (key->cert->type != SSH2_CERT_TYPE_USER &&
++	    key->cert->type != SSH2_CERT_TYPE_HOST) {
++		error("Unknown certificate type %u", key->cert->type);
++		goto out;
++	}
++
++	buffer_append(&tmp, principals, plen);
++	while (buffer_len(&tmp) > 0) {
++		if (key->cert->nprincipals >= CERT_MAX_PRINCIPALS) {
++			error("%s: Too many principals", __func__);
++			goto out;
++		}
++		if ((principal = buffer_get_string_ret(&tmp, &plen)) == NULL) {
++			error("%s: Principals data invalid", __func__);
++			goto out;
++		}
++		if (strlen(principal) != plen) {
++			error("%s: Principal contains \\0 character",
++			    __func__);
++			goto out;
++		}
++		key->cert->principals = xrealloc(key->cert->principals,
++		    key->cert->nprincipals + 1, sizeof(*key->cert->principals));
++		key->cert->principals[key->cert->nprincipals++] = principal;
++	}
++
++	buffer_clear(&tmp);
++
++	buffer_append(&key->cert->critical, critical, clen);
++	buffer_append(&tmp, critical, clen);
++	/* validate structure */
++	while (buffer_len(&tmp) != 0) {
++		if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL ||
++		    buffer_get_string_ptr_ret(&tmp, NULL) == NULL) {
++			error("%s: critical option data invalid", __func__);
++			goto out;
++		}
++	}
++	buffer_clear(&tmp);
++
++	buffer_append(&key->cert->extensions, exts, elen);
++	buffer_append(&tmp, exts, elen);
++	/* validate structure */
++	while (buffer_len(&tmp) != 0) {
++		if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL ||
++		    buffer_get_string_ptr_ret(&tmp, NULL) == NULL) {
++			error("%s: extension data invalid", __func__);
++			goto out;
++		}
++	}
++	buffer_clear(&tmp);
++
++	if ((key->cert->signature_key = key_from_blob(sig_key,
++	    sklen)) == NULL) {
++		error("%s: Signature key invalid", __func__);
++		goto out;
++	}
++	if (key->cert->signature_key->type != KEY_RSA &&
++	    key->cert->signature_key->type != KEY_DSA) {
++		error("%s: Invalid signature key type %s (%d)", __func__,
++		    key_type(key->cert->signature_key),
++		    key->cert->signature_key->type);
++		goto out;
++	}
++
++	switch (key_verify(key->cert->signature_key, sig, slen, 
++	    buffer_ptr(&key->cert->certblob), signed_len)) {
++	case 1:
++		ret = 0;
++		break; /* Good signature */
++	case 0:
++		error("%s: Invalid signature on certificate", __func__);
++		goto out;
++	case -1:
++		error("%s: Certificate signature verification failed",
++		    __func__);
++		goto out;
++	}
++
++ out:
++	buffer_free(&tmp);
++	if (principals != NULL)
++		xfree(principals);
++	if (critical != NULL)
++		xfree(critical);
++	if (exts != NULL)
++		xfree(exts);
++	if (sig_key != NULL)
++		xfree(sig_key);
++	if (sig != NULL)
++		xfree(sig);
++	return ret;
++}
++
+ Key *
+ key_from_blob(const u_char *blob, u_int blen)
+ {
+@@ -875,11 +1264,16 @@ key_from_blob(const u_char *blob, u_int
+ 	type = key_type_from_name(ktype);
+ 
+ 	switch (type) {
++	case KEY_RSA_CERT:
++		(void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
++		/* FALLTHROUGH */
+ 	case KEY_RSA:
++	case KEY_RSA_CERT_V00:
+ 		key = key_new(type);
+ 		if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 ||
+ 		    buffer_get_bignum2_ret(&b, key->rsa->n) == -1) {
+ 			error("key_from_blob: can't read rsa key");
++ badkey:
+ 			key_free(key);
+ 			key = NULL;
+ 			goto out;
+@@ -888,16 +1282,18 @@ key_from_blob(const u_char *blob, u_int
+ 		RSA_print_fp(stderr, key->rsa, 8);
+ #endif
+ 		break;
++	case KEY_DSA_CERT:
++		(void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
++		/* FALLTHROUGH */
+ 	case KEY_DSA:
++	case KEY_DSA_CERT_V00:
+ 		key = key_new(type);
+ 		if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 ||
+ 		    buffer_get_bignum2_ret(&b, key->dsa->q) == -1 ||
+ 		    buffer_get_bignum2_ret(&b, key->dsa->g) == -1 ||
+ 		    buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) {
+ 			error("key_from_blob: can't read dsa key");
+-			key_free(key);
+-			key = NULL;
+-			goto out;
++			goto badkey;
+ 		}
+ #ifdef DEBUG_PK
+ 		DSA_print_fp(stderr, key->dsa, 8);
+@@ -910,6 +1306,10 @@ key_from_blob(const u_char *blob, u_int
+ 		error("key_from_blob: cannot handle type %s", ktype);
+ 		goto out;
+ 	}
++	if (key_is_cert(key) && cert_parse(&b, key, blob, blen) == -1) {
++		error("key_from_blob: can't parse cert data");
++		goto badkey;
++	}
+ 	rlen = buffer_len(&b);
+ 	if (key != NULL && rlen != 0)
+ 		error("key_from_blob: remaining bytes in key blob %d", rlen);
+@@ -932,6 +1332,14 @@ key_to_blob(const Key *key, u_char **blo
+ 	}
+ 	buffer_init(&b);
+ 	switch (key->type) {
++	case KEY_DSA_CERT_V00:
++	case KEY_RSA_CERT_V00:
++	case KEY_DSA_CERT:
++	case KEY_RSA_CERT:
++		/* Use the existing blob */
++		buffer_append(&b, buffer_ptr(&key->cert->certblob),
++		    buffer_len(&key->cert->certblob));
++		break;
+ 	case KEY_DSA:
+ 		buffer_put_cstring(&b, key_ssh_name(key));
+ 		buffer_put_bignum2(&b, key->dsa->p);
+@@ -968,8 +1376,12 @@ key_sign(
+     const u_char *data, u_int datalen)
+ {
+ 	switch (key->type) {
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
+ 	case KEY_DSA:
+ 		return ssh_dss_sign(key, sigp, lenp, data, datalen);
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 	case KEY_RSA:
+ 		return ssh_rsa_sign(key, sigp, lenp, data, datalen);
+ 	default:
+@@ -992,8 +1404,12 @@ key_verify(
+ 		return -1;
+ 
+ 	switch (key->type) {
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
+ 	case KEY_DSA:
+ 		return ssh_dss_verify(key, signature, signaturelen, data, datalen);
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 	case KEY_RSA:
+ 		return ssh_rsa_verify(key, signature, signaturelen, data, datalen);
+ 	default:
+@@ -1015,6 +1431,10 @@ key_demote(const Key *k)
+ 	pk->rsa = NULL;
+ 
+ 	switch (k->type) {
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
++		key_cert_copy(k, pk);
++		/* FALLTHROUGH */
+ 	case KEY_RSA1:
+ 	case KEY_RSA:
+ 		if ((pk->rsa = RSA_new()) == NULL)
+@@ -1024,6 +1444,10 @@ key_demote(const Key *k)
+ 		if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL)
+ 			fatal("key_demote: BN_dup failed");
+ 		break;
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
++		key_cert_copy(k, pk);
++		/* FALLTHROUGH */
+ 	case KEY_DSA:
+ 		if ((pk->dsa = DSA_new()) == NULL)
+ 			fatal("key_demote: DSA_new failed");
+@@ -1063,3 +1487,244 @@ key_is_private(const Key *k)
+ 		return 1;
+ 	}
+ }
++
++int
++key_is_cert(const Key *k)
++{
++	if (k == NULL)
++		return 0;
++	switch (k->type) {
++	case KEY_RSA_CERT_V00:
++	case KEY_DSA_CERT_V00:
++	case KEY_RSA_CERT:
++	case KEY_DSA_CERT:
++		return 1;
++	default:
++		return 0;
++	}
++}
++
++/* Return the cert-less equivalent to a certified key type */
++int
++key_type_plain(int type)
++{
++	switch (type) {
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
++		return KEY_RSA;
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
++		return KEY_DSA;
++	default:
++		return type;
++	}
++}
++
++/* Convert a KEY_RSA or KEY_DSA to their _CERT equivalent */
++int
++key_to_certified(Key *k, int legacy)
++{
++	switch (k->type) {
++	case KEY_RSA:
++		k->cert = cert_new();
++		k->type = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT;
++		return 0;
++	case KEY_DSA:
++		k->cert = cert_new();
++		k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT;
++		return 0;
++	default:
++		error("%s: key has incorrect type %s", __func__, key_type(k));
++		return -1;
++	}
++}
++
++/* Convert a KEY_RSA_CERT or KEY_DSA_CERT to their raw key equivalent */
++int
++key_drop_cert(Key *k)
++{
++	switch (k->type) {
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
++		cert_free(k->cert);
++		k->type = KEY_RSA;
++		return 0;
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
++		cert_free(k->cert);
++		k->type = KEY_DSA;
++		return 0;
++	default:
++		error("%s: key has incorrect type %s", __func__, key_type(k));
++		return -1;
++	}
++}
++
++/* Sign a KEY_RSA_CERT or KEY_DSA_CERT, (re-)generating the signed certblob */
++int
++key_certify(Key *k, Key *ca)
++{
++	Buffer principals;
++	u_char *ca_blob, *sig_blob, nonce[32];
++	u_int i, ca_len, sig_len;
++
++	if (k->cert == NULL) {
++		error("%s: key lacks cert info", __func__);
++		return -1;
++	}
++
++	if (!key_is_cert(k)) {
++		error("%s: certificate has unknown type %d", __func__,
++		    k->cert->type);
++		return -1;
++	}
++
++	if (ca->type != KEY_RSA && ca->type != KEY_DSA) {
++		error("%s: CA key has unsupported type %s", __func__,
++		    key_type(ca));
++		return -1;
++	}
++
++	key_to_blob(ca, &ca_blob, &ca_len);
++
++	buffer_clear(&k->cert->certblob);
++	buffer_put_cstring(&k->cert->certblob, key_ssh_name(k));
++
++	/* -v01 certs put nonce first */
++	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) {
++		arc4random_buf(&nonce, sizeof(nonce));
++		buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
++	}
++
++	switch (k->type) {
++	case KEY_DSA_CERT_V00:
++	case KEY_DSA_CERT:
++		buffer_put_bignum2(&k->cert->certblob, k->dsa->p);
++		buffer_put_bignum2(&k->cert->certblob, k->dsa->q);
++		buffer_put_bignum2(&k->cert->certblob, k->dsa->g);
++		buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key);
++		break;
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
++		buffer_put_bignum2(&k->cert->certblob, k->rsa->e);
++		buffer_put_bignum2(&k->cert->certblob, k->rsa->n);
++		break;
++	default:
++		error("%s: key has incorrect type %s", __func__, key_type(k));
++		buffer_clear(&k->cert->certblob);
++		xfree(ca_blob);
++		return -1;
++	}
++
++	/* -v01 certs have a serial number next */
++	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT)
++		buffer_put_int64(&k->cert->certblob, k->cert->serial);
++
++	buffer_put_int(&k->cert->certblob, k->cert->type);
++	buffer_put_cstring(&k->cert->certblob, k->cert->key_id);
++
++	buffer_init(&principals);
++	for (i = 0; i < k->cert->nprincipals; i++)
++		buffer_put_cstring(&principals, k->cert->principals[i]);
++	buffer_put_string(&k->cert->certblob, buffer_ptr(&principals),
++	    buffer_len(&principals));
++	buffer_free(&principals);
++
++	buffer_put_int64(&k->cert->certblob, k->cert->valid_after);
++	buffer_put_int64(&k->cert->certblob, k->cert->valid_before);
++	buffer_put_string(&k->cert->certblob,
++	    buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical));
++
++	/* -v01 certs have non-critical options here */
++	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) {
++		buffer_put_string(&k->cert->certblob,
++		    buffer_ptr(&k->cert->extensions),
++		    buffer_len(&k->cert->extensions));
++	}
++
++	/* -v00 certs put the nonce at the end */
++	if (k->type == KEY_DSA_CERT_V00 || k->type == KEY_RSA_CERT_V00)
++		buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
++
++	buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */
++	buffer_put_string(&k->cert->certblob, ca_blob, ca_len);
++	xfree(ca_blob);
++
++	/* Sign the whole mess */
++	if (key_sign(ca, &sig_blob, &sig_len, buffer_ptr(&k->cert->certblob),
++	    buffer_len(&k->cert->certblob)) != 0) {
++		error("%s: signature operation failed", __func__);
++		buffer_clear(&k->cert->certblob);
++		return -1;
++	}
++	/* Append signature and we are done */
++	buffer_put_string(&k->cert->certblob, sig_blob, sig_len);
++	xfree(sig_blob);
++
++	return 0;
++}
++
++int
++key_cert_check_authority(const Key *k, int want_host, int require_principal,
++    const char *name, const char **reason)
++{
++	u_int i, principal_matches;
++	time_t now = time(NULL);
++
++	if (want_host) {
++		if (k->cert->type != SSH2_CERT_TYPE_HOST) {
++			*reason = "Certificate invalid: not a host certificate";
++			return -1;
++		}
++	} else {
++		if (k->cert->type != SSH2_CERT_TYPE_USER) {
++			*reason = "Certificate invalid: not a user certificate";
++			return -1;
++		}
++	}
++	if (now < 0) {
++		error("%s: system clock lies before epoch", __func__);
++		*reason = "Certificate invalid: not yet valid";
++		return -1;
++	}
++	if ((u_int64_t)now < k->cert->valid_after) {
++		*reason = "Certificate invalid: not yet valid";
++		return -1;
++	}
++	if ((u_int64_t)now >= k->cert->valid_before) {
++		*reason = "Certificate invalid: expired";
++		return -1;
++	}
++	if (k->cert->nprincipals == 0) {
++		if (require_principal) {
++			*reason = "Certificate lacks principal list";
++			return -1;
++		}
++	} else if (name != NULL) {
++		principal_matches = 0;
++		for (i = 0; i < k->cert->nprincipals; i++) {
++			if (strcmp(name, k->cert->principals[i]) == 0) {
++				principal_matches = 1;
++				break;
++			}
++		}
++		if (!principal_matches) {
++			*reason = "Certificate invalid: name is not a listed "
++			    "principal";
++			return -1;
++		}
++	}
++	return 0;
++}
++
++int
++key_cert_is_legacy(Key *k)
++{
++	switch (k->type) {
++	case KEY_DSA_CERT_V00:
++	case KEY_RSA_CERT_V00:
++		return 1;
++	default:
++		return 0;
++	}
++}
+diff -up openssh-5.3p1/key.h.certificates openssh-5.3p1/key.h
+--- openssh-5.3p1/key.h.certificates	2013-07-18 15:38:20.334186856 +0200
++++ openssh-5.3p1/key.h	2013-07-18 15:38:20.437186407 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: key.h,v 1.27 2008/06/11 21:01:35 grunk Exp $ */
++/* $OpenBSD: key.h,v 1.30 2010/04/16 01:47:26 djm Exp $ */
+ 
+ /*
+  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
+@@ -26,6 +26,7 @@
+ #ifndef KEY_H
+ #define KEY_H
+ 
++#include "buffer.h"
+ #include <openssl/rsa.h>
+ #include <openssl/dsa.h>
+ 
+@@ -40,6 +41,10 @@ enum types {
+ 	KEY_RSA,
+ 	KEY_DSA,
+ 	KEY_NSS,
++	KEY_RSA_CERT,
++	KEY_DSA_CERT,
++	KEY_RSA_CERT_V00,
++	KEY_DSA_CERT_V00,
+ 	KEY_NULL,
+ 	KEY_UNSPEC
+ };
+@@ -57,6 +62,20 @@ enum fp_rep {
+ #define KEY_FLAG_EXT		0x0001
+ #define KEY_FLAG_NSS		0x0002
+ 
++#define CERT_MAX_PRINCIPALS	256
++struct KeyCert {
++	Buffer		 certblob; /* Kept around for use on wire */
++	u_int		 type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */
++	u_int64_t	 serial;
++	char		*key_id;
++	u_int		 nprincipals;
++	char		**principals;
++	u_int64_t	 valid_after, valid_before;
++	Buffer		 critical;
++	Buffer		 extensions;
++	Key		*signature_key;
++};
++
+ #ifdef HAVE_LIBNSS
+ typedef struct NSSKey NSSKey;
+ struct NSSKey {
+@@ -70,21 +89,25 @@ struct Key {
+ 	int	 flags;
+ 	RSA	*rsa;
+ 	DSA	*dsa;
++	struct KeyCert *cert;
+ #ifdef HAVE_LIBNSS
+ 	NSSKey  *nss;
+ #endif
+ };
+ 
+ Key		*key_new(int);
++void		 key_add_private(Key *);
+ Key		*key_new_private(int);
+ Key 		*key_new_nss(int);
+ Key		*key_new_nss_copy(int, const Key *);
+ void		 key_free(Key *);
+ Key		*key_demote(const Key *);
++int		 key_equal_public(const Key *, const Key *);
+ int		 key_equal(const Key *, const Key *);
+-char		*key_fingerprint(const Key *, enum fp_type, enum fp_rep);
+-u_char		*key_fingerprint_raw(const Key *, enum fp_type, u_int *);
++char		*key_fingerprint(Key *, enum fp_type, enum fp_rep);
++u_char		*key_fingerprint_raw(Key *, enum fp_type, u_int *);
+ const char	*key_type(const Key *);
++const char	*key_cert_type(const Key *);
+ int		 key_write(const Key *, FILE *);
+ int		 key_read(Key *, char **);
+ u_int		 key_size(const Key *);
+@@ -92,6 +115,15 @@ u_int		 key_size(const Key *);
+ Key	*key_generate(int, u_int);
+ Key	*key_from_private(const Key *);
+ int	 key_type_from_name(char *);
++int	 key_is_cert(const Key *);
++int	 key_type_plain(int);
++int	 key_to_certified(Key *, int);
++int	 key_drop_cert(Key *);
++int	 key_certify(Key *, Key *);
++void	 key_cert_copy(const Key *, struct Key *);
++int	 key_cert_check_authority(const Key *, int, int, const char *,
++	    const char **);
++int	 key_cert_is_legacy(Key *);
+ 
+ Key		*key_from_blob(const u_char *, u_int);
+ int		 key_to_blob(const Key *, u_char **, u_int *);
+diff -up openssh-5.3p1/match.h.certificates openssh-5.3p1/match.h
+--- openssh-5.3p1/match.h.certificates	2008-06-10 14:59:10.000000000 +0200
++++ openssh-5.3p1/match.h	2013-07-18 15:38:20.437186407 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: match.h,v 1.14 2008/06/10 03:57:27 djm Exp $ */
++/* $OpenBSD: match.h,v 1.15 2010/02/26 20:29:54 djm Exp $ */
+ 
+ /*
+  * Author: Tatu Ylonen <ylo at cs.hut.fi>
+@@ -23,5 +23,5 @@ char	*match_list(const char *, const cha
+ 
+ /* addrmatch.c */
+ int	 addr_match_list(const char *, const char *);
+-
++int	 addr_match_cidr_list(const char *, const char *);
+ #endif
+diff -up openssh-5.3p1/monitor.c.certificates openssh-5.3p1/monitor.c
+--- openssh-5.3p1/monitor.c.certificates	2013-07-18 15:38:20.386186629 +0200
++++ openssh-5.3p1/monitor.c	2013-07-18 15:38:20.438186403 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: monitor.c,v 1.104 2009/06/12 20:43:22 andreas Exp $ */
++/* $OpenBSD: monitor.c,v 1.105 2010/02/26 20:29:54 djm Exp $ */
+ /*
+  * Copyright 2002 Niels Provos <provos at citi.umich.edu>
+  * Copyright 2002 Markus Friedl <markus at openbsd.org>
+@@ -1891,7 +1891,8 @@ mm_get_kex(Buffer *m)
+ 	kex->flags = buffer_get_int(m);
+ 	kex->client_version_string = buffer_get_string(m, NULL);
+ 	kex->server_version_string = buffer_get_string(m, NULL);
+-	kex->load_host_key=&get_hostkey_by_type;
++	kex->load_host_public_key=&get_hostkey_public_by_type;
++	kex->load_host_private_key=&get_hostkey_private_by_type;
+ 	kex->host_key_index=&get_hostkey_index;
+ 
+ 	return (kex);
+diff -up openssh-5.3p1/myproposal.h.certificates openssh-5.3p1/myproposal.h
+--- openssh-5.3p1/myproposal.h.certificates	2013-07-18 15:38:20.422186472 +0200
++++ openssh-5.3p1/myproposal.h	2013-07-18 15:38:20.438186403 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: myproposal.h,v 1.23 2009/01/23 07:58:11 djm Exp $ */
++/* $OpenBSD: myproposal.h,v 1.25 2010/04/16 01:47:26 djm Exp $ */
+ 
+ /*
+  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+@@ -40,7 +40,12 @@
+ 	"diffie-hellman-group1-sha1"
+ #endif
+ 
+-#define	KEX_DEFAULT_PK_ALG	"ssh-rsa,ssh-dss"
++#define	KEX_DEFAULT_PK_ALG	\
++				"ssh-rsa-cert-v01 at openssh.com," \
++				"ssh-dss-cert-v01 at openssh.com," \
++				"ssh-rsa-cert-v00 at openssh.com," \
++ 				"ssh-dss-cert-v00 at openssh.com," \
++ 				"ssh-rsa,ssh-dss"
+ 
+ #define	KEX_DEFAULT_ENCRYPT \
+ 	"aes128-ctr,aes192-ctr,aes256-ctr," \
+diff -up openssh-5.3p1/PROTOCOL.agent.certificates openssh-5.3p1/PROTOCOL.agent
+--- openssh-5.3p1/PROTOCOL.agent.certificates	2008-07-02 14:35:00.000000000 +0200
++++ openssh-5.3p1/PROTOCOL.agent	2013-07-18 15:38:20.439186398 +0200
+@@ -173,6 +173,15 @@ be added using the following request
+ 	string			key_comment
+ 	constraint[]		key_constraints
+ 
++DSA certificates may be added with:
++	byte			SSH2_AGENTC_ADD_IDENTITY or
++				SSH2_AGENTC_ADD_ID_CONSTRAINED
++	string			"ssh-dss-cert-v00 at openssh.com"
++	string			certificate
++	mpint			dsa_private_key
++	string			key_comment
++	constraint[]		key_constraints
++
+ RSA keys may be added with this request:
+ 
+ 	byte			SSH2_AGENTC_ADD_IDENTITY or
+@@ -187,6 +196,19 @@ RSA keys may be added with this request:
+ 	string			key_comment
+ 	constraint[]		key_constraints
+ 
++RSA certificates may be added with this request:
++
++	byte			SSH2_AGENTC_ADD_IDENTITY or
++				SSH2_AGENTC_ADD_ID_CONSTRAINED
++	string			"ssh-rsa-cert-v00 at openssh.com"
++	string			certificate
++	mpint			rsa_d
++	mpint			rsa_iqmp
++	mpint			rsa_p
++	mpint			rsa_q
++	string			key_comment
++	constraint[]		key_constraints
++
+ Note that the 'rsa_p' and 'rsa_q' parameters are sent in the reverse
+ order to the protocol 1 add keys message. As with the corresponding
+ protocol 1 "add key" request, the private key is overspecified to avoid
+@@ -513,4 +535,4 @@ Locking and unlocking affects both proto
+ 	SSH_AGENT_CONSTRAIN_LIFETIME			1
+ 	SSH_AGENT_CONSTRAIN_CONFIRM			2
+ 
+-$OpenBSD: PROTOCOL.agent,v 1.4 2008/07/01 23:12:47 stevesk Exp $
++$OpenBSD: PROTOCOL.agent,v 1.5 2010/02/26 20:29:54 djm Exp $
+diff -up openssh-5.3p1/PROTOCOL.certificates openssh-5.3p1/PROTOCOL
+--- openssh-5.3p1/PROTOCOL.certificates	2009-02-14 08:00:52.000000000 +0100
++++ openssh-5.3p1/PROTOCOL	2013-07-18 15:38:20.438186403 +0200
+@@ -31,7 +31,14 @@ The method is documented in:
+ 
+ http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt
+ 
+-3. connection: Channel write close extension "eow at openssh.com"
++3. transport: New public key algorithms "ssh-rsa-cert-v00 at openssh.com" and
++   "ssh-dsa-cert-v00 at openssh.com"
++
++OpenSSH introduces two new public key algorithms to support certificate
++authentication for users and hostkeys. These methods are documented in
++the file PROTOCOL.certkeys
++
++4. connection: Channel write close extension "eow at openssh.com"
+ 
+ The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF
+ message to allow an endpoint to signal its peer that it will send no
+@@ -70,7 +77,7 @@ message is only sent to OpenSSH peers (i
+ Other SSH implementations may be whitelisted to receive this message
+ upon request.
+ 
+-4. connection: disallow additional sessions extension
++5. connection: disallow additional sessions extension
+    "no-more-sessions at openssh.com"
+ 
+ Most SSH connections will only ever request a single session, but a
+@@ -98,7 +105,7 @@ of this message, the no-more-sessions re
+ servers (identified by banner). Other SSH implementations may be
+ whitelisted to receive this message upon request.
+ 
+-5. connection: Tunnel forward extension "tun at openssh.com"
++6. connection: Tunnel forward extension "tun at openssh.com"
+ 
+ OpenSSH supports layer 2 and layer 3 tunnelling via the "tun at openssh.com"
+ channel type. This channel type supports forwarding of network packets
+@@ -159,7 +166,7 @@ The contents of the "data" field for lay
+ The "frame" field contains an IEEE 802.3 Ethernet frame, including
+ header.
+ 
+-6. sftp: Reversal of arguments to SSH_FXP_SYMLINK
++7. sftp: Reversal of arguments to SSH_FXP_SYMLINK
+ 
+ When OpenSSH's sftp-server was implemented, the order of the arguments
+ to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately,
+@@ -172,7 +179,7 @@ SSH_FXP_SYMLINK as follows:
+ 	string		targetpath
+ 	string		linkpath
+ 
+-7. sftp: Server extension announcement in SSH_FXP_VERSION
++8. sftp: Server extension announcement in SSH_FXP_VERSION
+ 
+ OpenSSH's sftp-server lists the extensions it supports using the
+ standard extension announcement mechanism in the SSH_FXP_VERSION server
+@@ -193,7 +200,7 @@ ever changed in an incompatible way. The
+ extension with multiple versions (though this is unlikely). Clients MUST
+ check the version number before attempting to use the extension.
+ 
+-8. sftp: Extension request "posix-rename at openssh.com"
++9. sftp: Extension request "posix-rename at openssh.com"
+ 
+ This operation provides a rename operation with POSIX semantics, which
+ are different to those provided by the standard SSH_FXP_RENAME in
+@@ -210,7 +217,7 @@ rename(oldpath, newpath) and will respon
+ This extension is advertised in the SSH_FXP_VERSION hello with version
+ "1".
+ 
+-9. sftp: Extension requests "statvfs at openssh.com" and
++10. sftp: Extension requests "statvfs at openssh.com" and
+          "fstatvfs at openssh.com"
+ 
+ These requests correspond to the statvfs and fstatvfs POSIX system
+diff -up openssh-5.3p1/PROTOCOL.certkeys.certificates openssh-5.3p1/PROTOCOL.certkeys
+--- openssh-5.3p1/PROTOCOL.certkeys.certificates	2013-07-18 15:38:20.439186398 +0200
++++ openssh-5.3p1/PROTOCOL.certkeys	2013-07-18 15:38:20.439186398 +0200
+@@ -0,0 +1,221 @@
++This document describes a simple public-key certificate authentication
++system for use by SSH.
++
++Background
++----------
++
++The SSH protocol currently supports a simple public key authentication
++mechanism. Unlike other public key implementations, SSH eschews the
++use of X.509 certificates and uses raw keys. This approach has some
++benefits relating to simplicity of configuration and minimisation
++of attack surface, but it does not support the important use-cases
++of centrally managed, passwordless authentication and centrally
++certified host keys.
++
++These protocol extensions build on the simple public key authentication
++system already in SSH to allow certificate-based authentication.
++The certificates used are not traditional X.509 certificates, with
++numerous options and complex encoding rules, but something rather
++more minimal: a key, some identity information and usage options
++that have been signed with some other trusted key.
++
++A sshd server may be configured to allow authentication via certified
++keys, by extending the existing ~/.ssh/authorized_keys mechanism
++to allow specification of certification authority keys in addition
++to raw user keys. The ssh client will support automatic verification
++of acceptance of certified host keys, by adding a similar ability
++to specify CA keys in ~/.ssh/known_hosts.
++
++Certified keys are represented using two new key types:
++ssh-rsa-cert-v01 at openssh.com and ssh-dss-cert-v01 at openssh.com that
++include certification information along with the public key that is used
++to sign challenges. ssh-keygen performs the CA signing operation.
++
++Protocol extensions
++-------------------
++
++The SSH wire protocol includes several extensibility mechanisms.
++These modifications shall take advantage of namespaced public key
++algorithm names to add support for certificate authentication without
++breaking the protocol - implementations that do not support the
++extensions will simply ignore them.
++
++Authentication using the new key formats described below proceeds
++using the existing SSH "publickey" authentication method described
++in RFC4252 section 7.
++
++New public key formats
++----------------------
++
++The ssh-rsa-cert-v01 at openssh.com and ssh-dss-cert-v01 at openssh.com key
++types take a similar high-level format (note: data types and
++encoding are as per RFC4251 section 5). The serialised wire encoding of
++these certificates is also used for storing them on disk.
++
++#define SSH_CERT_TYPE_USER    1
++#define SSH_CERT_TYPE_HOST    2
++
++RSA certificate
++
++    string    "ssh-rsa-cert-v01 at openssh.com"
++    string    nonce
++    mpint     e
++    mpint     n
++    uint64    serial
++    uint32    type
++    string    key id
++    string    valid principals
++    uint64    valid after
++    uint64    valid before
++    string    critical options
++    string    extensions
++    string    reserved
++    string    signature key
++    string    signature
++
++DSA certificate
++
++    string    "ssh-dss-cert-v01 at openssh.com"
++    string    nonce
++    mpint     p
++    mpint     q
++    mpint     g
++    mpint     y
++    uint64    serial
++    uint32    type
++    string    key id
++    string    valid principals
++    uint64    valid after
++    uint64    valid before
++    string    critical options
++    string    extensions
++    string    reserved
++    string    signature key
++    string    signature
++
++The nonce field is a CA-provided random bitstring of arbitrary length
++(but typically 16 or 32 bytes) included to make attacks that depend on
++inducing collisions in the signature hash infeasible.
++
++e and n are the RSA exponent and public modulus respectively.
++
++p, q, g, y are the DSA parameters as described in FIPS-186-2.
++
++serial is an optional certificate serial number set by the CA to
++provide an abbreviated way to refer to certificates from that CA.
++If a CA does not with to number its certificates it must set this
++field to zero.
++
++type specifies whether this certificate is for identification of a user
++or a host using a SSH_CERT_TYPE_... value.
++
++key id is a free-form text field that is filled in by the CA at the time
++of signing; the intention is that the contents of this field are used to
++identify the identity principal in log messages.
++
++"valid principals" is a string containing zero or more principals as
++strings packed inside it. These principals list the names for which this
++certificate is valid; hostnames for SSH_CERT_TYPE_HOST certificates and
++usernames for SSH_CERT_TYPE_USER certificates. As a special case, a
++zero-length "valid principals" field means the certificate is valid for
++any principal of the specified type. XXX DNS wildcards?
++
++"valid after" and "valid before" specify a validity period for the
++certificate. Each represents a time in seconds since 1970-01-01
++00:00:00. A certificate is considered valid if:
++	 valid after <= current time < valid before
++
++criticial options is a set of zero or more key options encoded as
++below. All such options are "critical" in the sense that an implementation
++must refuse to authorise a key that has an unrecognised option.
++
++extensions is a set of zero or more optional extensions. These extensions
++are not critical, and an implementation that encounters one that it does
++not recognise may safely ignore it.
++
++The reserved field is currently unused and is ignored in this version of
++the protocol.
++
++signature key contains the CA key used to sign the certificate.
++The valid key types for CA keys are ssh-rsa and ssh-dss. "Chained"
++certificates, where the signature key type is a certificate type itself
++are NOT supported. Note that it is possible for a RSA certificate key to
++be signed by a DSS CA key and vice-versa.
++
++signature is computed over all preceding fields from the initial string
++up to, and including the signature key. Signatures are computed and
++encoded according to the rules defined for the CA's public key algorithm
++(RFC4253 section 6.6 for ssh-rsa and ssh-dss).
++
++Critical options
++----------------
++
++The critical options section of the certificate specifies zero or more
++options on the certificates validity. The format of this field
++is a sequence of zero or more tuples:
++
++    string       name
++    string       data
++
++The name field identifies the option and the data field encodes
++option-specific information (see below). All options are
++"critical", if an implementation does not recognise a option
++then the validating party should refuse to accept the certificate.
++
++The supported options and the contents and structure of their
++data fields are:
++
++Name                    Format        Description
++-----------------------------------------------------------------------------
++force-command           string        Specifies a command that is executed
++                                      (replacing any the user specified on the
++                                      ssh command-line) whenever this key is
++                                      used for authentication.
++
++source-address          string        Comma-separated list of source addresses
++                                      from which this certificate is accepted
++                                      for authentication. Addresses are
++                                      specified in CIDR format (nn.nn.nn.nn/nn
++                                      or hhhh::hhhh/nn).
++                                      If this option is not present then
++                                      certificates may be presented from any
++                                      source address.
++
++Extensions
++----------
++
++The extensions section of the certificate specifies zero or more
++non-critical certificate extensions. The encoding of extensions in this
++field is identical to that of the critical options. If an implementation
++does not recognise an extension, then it should ignore it.
++
++The supported extensions and the contents and structure of their data
++fields are:
++
++Name                    Format        Description
++-----------------------------------------------------------------------------
++permit-X11-forwarding   empty         Flag indicating that X11 forwarding
++                                      should be permitted. X11 forwarding will
++                                      be refused if this option is absent.
++
++permit-agent-forwarding empty         Flag indicating that agent forwarding
++                                      should be allowed. Agent forwarding
++                                      must not be permitted unless this
++                                      option is present.
++
++permit-port-forwarding  empty         Flag indicating that port-forwarding
++                                      should be allowed. If this option is
++                                      not present then no port forwarding will
++                                      be allowed.
++
++permit-pty              empty         Flag indicating that PTY allocation
++                                      should be permitted. In the absence of
++                                      this option PTY allocation will be
++                                      disabled.
++
++permit-user-rc          empty         Flag indicating that execution of
++                                      ~/.ssh/rc should be permitted. Execution
++                                      of this script will not be permitted if
++                                      this option is not present.
++
++$OpenBSD: PROTOCOL.certkeys,v 1.4 2010/04/16 01:47:25 djm Exp $
+diff -up openssh-5.3p1/regress/cert-hostkey.sh.certificates openssh-5.3p1/regress/cert-hostkey.sh
+--- openssh-5.3p1/regress/cert-hostkey.sh.certificates	2013-07-18 15:38:20.439186398 +0200
++++ openssh-5.3p1/regress/cert-hostkey.sh	2013-07-18 15:38:20.439186398 +0200
+@@ -0,0 +1,239 @@
++#	$OpenBSD: cert-hostkey.sh,v 1.4 2010/04/16 01:58:45 djm Exp $
++#	Placed in the Public Domain.
++
++tid="certified host keys"
++
++rm -f $OBJ/known_hosts-cert $OBJ/host_ca_key* $OBJ/cert_host_key*
++cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak
++
++HOSTS='localhost-with-alias,127.0.0.1,::1'
++
++# Create a CA key and add it to known hosts
++${SSHKEYGEN} -q -N '' -t rsa  -f $OBJ/host_ca_key ||\
++	fail "ssh-keygen of host_ca_key failed"
++(
++	echon '@cert-authority '
++	echon "$HOSTS "
++	cat $OBJ/host_ca_key.pub
++) > $OBJ/known_hosts-cert
++
++# Generate and sign host keys
++for ktype in rsa dsa ; do 
++	verbose "$tid: sign host ${ktype} cert"
++	# Generate and sign a host key
++	${SSHKEYGEN} -q -N '' -t ${ktype} \
++	    -f $OBJ/cert_host_key_${ktype} || \
++		fail "ssh-keygen of cert_host_key_${ktype} failed"
++	${SSHKEYGEN} -h -q -s $OBJ/host_ca_key \
++	    -I "regress host key for $USER" \
++	    -Z $HOSTS $OBJ/cert_host_key_${ktype} ||
++		fail "couldn't sign cert_host_key_${ktype}"
++	cp $OBJ/cert_host_key_${ktype} $OBJ/cert_host_key_${ktype}_v00
++	cp $OBJ/cert_host_key_${ktype}.pub $OBJ/cert_host_key_${ktype}_v00.pub
++	${SSHKEYGEN} -t v00 -h -q -s $OBJ/host_ca_key \
++	    -I "regress host key for $USER" \
++	    -Z $HOSTS $OBJ/cert_host_key_${ktype}_v00 ||
++		fail "couldn't sign cert_host_key_${ktype}_v00"
++done
++
++# Basic connect tests
++for privsep in yes no ; do
++	for ktype in rsa dsa rsa_v00 dsa_v00; do 
++		verbose "$tid: host ${ktype} cert connect privsep $privsep"
++		(
++			cat $OBJ/sshd_proxy_bak
++			echo HostKey $OBJ/cert_host_key_${ktype}
++			echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
++			echo UsePrivilegeSeparation $privsep
++		) > $OBJ/sshd_proxy
++
++		${SSH} -2 -oUserKnownHostsFile=$OBJ/known_hosts-cert \
++		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
++			-F $OBJ/ssh_proxy somehost true
++		if [ $? -ne 0 ]; then
++			fail "ssh cert connect failed"
++		fi
++	done
++done
++
++# Revoked certificates with key present
++(
++	echon '@cert-authority '
++	echon "$HOSTS "
++	cat $OBJ/host_ca_key.pub
++	echon '@revoked '
++	echon "* "
++	cat $OBJ/cert_host_key_rsa.pub
++	echon '@revoked '
++	echon "* "
++	cat $OBJ/cert_host_key_dsa.pub
++	echon '@revoked '
++	echon "* "
++	cat $OBJ/cert_host_key_rsa_v00.pub
++	echon '@revoked '
++	echon "* "
++	cat $OBJ/cert_host_key_dsa_v00.pub
++) > $OBJ/known_hosts-cert
++for privsep in yes no ; do
++	for ktype in rsa dsa rsa_v00 dsa_v00; do 
++		verbose "$tid: host ${ktype} revoked cert privsep $privsep"
++		(
++			cat $OBJ/sshd_proxy_bak
++			echo HostKey $OBJ/cert_host_key_${ktype}
++			echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
++			echo UsePrivilegeSeparation $privsep
++		) > $OBJ/sshd_proxy
++
++		${SSH} -2 -oUserKnownHostsFile=$OBJ/known_hosts-cert \
++		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
++			-F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++		if [ $? -eq 0 ]; then
++			fail "ssh cert connect succeeded unexpectedly"
++		fi
++	done
++done
++
++# Revoked CA
++(
++	echon '@cert-authority '
++	echon "$HOSTS "
++	cat $OBJ/host_ca_key.pub
++	echon '@revoked '
++	echon "* "
++	cat $OBJ/host_ca_key.pub
++) > $OBJ/known_hosts-cert
++for ktype in rsa dsa rsa_v00 dsa_v00 ; do 
++	verbose "$tid: host ${ktype} revoked cert"
++	(
++		cat $OBJ/sshd_proxy_bak
++		echo HostKey $OBJ/cert_host_key_${ktype}
++		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
++	) > $OBJ/sshd_proxy
++	${SSH} -2 -oUserKnownHostsFile=$OBJ/known_hosts-cert \
++	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
++		-F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++	if [ $? -eq 0 ]; then
++		fail "ssh cert connect succeeded unexpectedly"
++	fi
++done
++
++# Create a CA key and add it to known hosts
++(
++	echon '@cert-authority '
++	echon "$HOSTS "
++	cat $OBJ/host_ca_key.pub
++) > $OBJ/known_hosts-cert
++
++test_one() {
++	ident=$1
++	result=$2
++	sign_opts=$3
++
++	for kt in rsa rsa_v00 ; do
++		case $kt in
++		*_v00) args="-t v00" ;;
++		*) args="" ;;
++		esac
++
++		verbose "$tid: host cert connect $ident $kt expect $result"
++		${SSHKEYGEN} -q -s $OBJ/host_ca_key \
++		    -I "regress host key for $USER" \
++		    $sign_opts $args \
++		    $OBJ/cert_host_key_${kt} ||
++			fail "couldn't sign cert_host_key_${kt}"
++		(
++			cat $OBJ/sshd_proxy_bak
++			echo HostKey $OBJ/cert_host_key_${kt}
++			echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub
++		) > $OBJ/sshd_proxy
++	
++		${SSH} -2 -oUserKnownHostsFile=$OBJ/known_hosts-cert \
++		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
++		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++		rc=$?
++		if [ "x$result" = "xsuccess" ] ; then
++			if [ $rc -ne 0 ]; then
++				fail "ssh cert connect $ident failed unexpectedly"
++			fi
++		else
++			if [ $rc -eq 0 ]; then
++				fail "ssh cert connect $ident succeeded unexpectedly"
++			fi
++		fi
++	done
++}
++
++test_one "user-certificate"	failure "-Z $HOSTS"
++test_one "empty principals"	success "-h"
++test_one "wrong principals"	failure "-h -Z foo"
++test_one "cert not yet valid"	failure "-h -V20200101:20300101"
++test_one "cert expired"		failure "-h -V19800101:19900101"
++test_one "cert valid interval"	success "-h -V-1w:+2w"
++test_one "cert has constraints"	failure "-h -Oforce-command=false"
++
++# Check downgrade of cert to raw key when no CA found
++for v in v01 v00 ;  do 
++	for ktype in rsa dsa ; do 
++		rm -f $OBJ/known_hosts-cert $OBJ/cert_host_key*
++		verbose "$tid: host ${ktype} ${v} cert downgrade to raw key"
++		# Generate and sign a host key
++		${SSHKEYGEN} -q -N '' -t ${ktype} \
++		    -f $OBJ/cert_host_key_${ktype} || \
++			fail "ssh-keygen of cert_host_key_${ktype} failed"
++		${SSHKEYGEN} -t ${v} -h -q -s $OBJ/host_ca_key \
++		    -I "regress host key for $USER" \
++		    -Z $HOSTS $OBJ/cert_host_key_${ktype} ||
++			fail "couldn't sign cert_host_key_${ktype}"
++		(
++			echon "$HOSTS "
++			cat $OBJ/cert_host_key_${ktype}.pub
++		) > $OBJ/known_hosts-cert
++		(
++			cat $OBJ/sshd_proxy_bak
++			echo HostKey $OBJ/cert_host_key_${ktype}
++			echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
++		) > $OBJ/sshd_proxy
++		
++		${SSH} -2 -oUserKnownHostsFile=$OBJ/known_hosts-cert \
++		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
++			-F $OBJ/ssh_proxy somehost true
++		if [ $? -ne 0 ]; then
++			fail "ssh cert connect failed"
++		fi
++	done
++done
++
++# Wrong certificate
++(
++	echon '@cert-authority '
++	echon "$HOSTS "
++	cat $OBJ/host_ca_key.pub
++) > $OBJ/known_hosts-cert
++for v in v01 v00 ;  do 
++	for kt in rsa dsa ; do 
++		rm -f $OBJ/cert_host_key*
++		# Self-sign key
++		${SSHKEYGEN} -q -N '' -t ${kt} \
++		    -f $OBJ/cert_host_key_${kt} || \
++			fail "ssh-keygen of cert_host_key_${kt} failed"
++		${SSHKEYGEN} -t ${v} -h -q -s $OBJ/cert_host_key_${kt} \
++		    -I "regress host key for $USER" \
++		    -Z $HOSTS $OBJ/cert_host_key_${kt} ||
++			fail "couldn't sign cert_host_key_${kt}"
++		verbose "$tid: host ${kt} connect wrong cert"
++		(
++			cat $OBJ/sshd_proxy_bak
++			echo HostKey $OBJ/cert_host_key_${kt}
++			echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub
++		) > $OBJ/sshd_proxy
++	
++		${SSH} -2 -oUserKnownHostsFile=$OBJ/known_hosts-cert \
++		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
++			-F $OBJ/ssh_proxy -q somehost true >/dev/null 2>&1
++		if [ $? -eq 0 ]; then
++			fail "ssh cert connect $ident succeeded unexpectedly"
++		fi
++	done
++done
++
++rm -f $OBJ/known_hosts-cert $OBJ/host_ca_key* $OBJ/cert_host_key*
+diff -up openssh-5.3p1/regress/cert-userkey.sh.certificates openssh-5.3p1/regress/cert-userkey.sh
+--- openssh-5.3p1/regress/cert-userkey.sh.certificates	2013-07-18 15:38:20.439186398 +0200
++++ openssh-5.3p1/regress/cert-userkey.sh	2013-07-18 15:38:20.439186398 +0200
+@@ -0,0 +1,295 @@
++#	$OpenBSD: cert-userkey.sh,v 1.5 2010/05/07 11:31:26 djm Exp $
++#	Placed in the Public Domain.
++
++tid="certified user keys"
++
++rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/cert_user_key*
++cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak
++
++# Create a CA key
++${SSHKEYGEN} -q -N '' -t rsa  -f $OBJ/user_ca_key ||\
++	fail "ssh-keygen of user_ca_key failed"
++
++# Generate and sign user keys
++for ktype in rsa dsa ; do 
++	verbose "$tid: sign user ${ktype} cert"
++	${SSHKEYGEN} -q -N '' -t ${ktype} \
++	    -f $OBJ/cert_user_key_${ktype} || \
++		fail "ssh-keygen of cert_user_key_${ktype} failed"
++	${SSHKEYGEN} -q -s $OBJ/user_ca_key -I \
++	    "regress user key for $USER" \
++	    -Z ${USER},mekmitasdigoat $OBJ/cert_user_key_${ktype} ||
++		fail "couldn't sign cert_user_key_${ktype}"
++	cp $OBJ/cert_user_key_${ktype} $OBJ/cert_user_key_${ktype}_v00
++	cp $OBJ/cert_user_key_${ktype}.pub $OBJ/cert_user_key_${ktype}_v00.pub
++	${SSHKEYGEN} -q -t v00 -s $OBJ/user_ca_key -I \
++	    "regress user key for $USER" \
++	    -Z ${USER},mekmitasdigoat $OBJ/cert_user_key_${ktype}_v00 ||
++		fail "couldn't sign cert_user_key_${ktype}_v00"
++done
++
++# Test explicitly-specified principals
++for ktype in rsa dsa rsa_v00 dsa_v00 ; do 
++	for privsep in yes no ; do
++		_prefix="${ktype} privsep $privsep"
++
++		# Setup for AuthorizedPrincipalsFile
++		rm -f $OBJ/authorized_keys_$USER
++		(
++			cat $OBJ/sshd_proxy_bak
++			echo "UsePrivilegeSeparation $privsep"
++			echo "AuthorizedPrincipalsFile " \
++			    "$OBJ/authorized_principals_%u"
++			echo "TrustedUserCAKeys $OBJ/user_ca_key.pub"
++		) > $OBJ/sshd_proxy
++
++		# Missing authorized_principals
++		verbose "$tid: ${_prefix} missing authorized_principals"
++		rm -f $OBJ/authorized_principals_$USER
++		${SSH} -2i $OBJ/cert_user_key_${ktype} \
++		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++		if [ $? -eq 0 ]; then
++			fail "ssh cert connect succeeded unexpectedly"
++		fi
++
++		# Empty authorized_principals
++		verbose "$tid: ${_prefix} empty authorized_principals"
++		echo > $OBJ/authorized_principals_$USER
++		${SSH} -2i $OBJ/cert_user_key_${ktype} \
++		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++		if [ $? -eq 0 ]; then
++			fail "ssh cert connect succeeded unexpectedly"
++		fi
++	
++		# Wrong authorized_principals
++		verbose "$tid: ${_prefix} wrong authorized_principals"
++		echo gregorsamsa > $OBJ/authorized_principals_$USER
++		${SSH} -2i $OBJ/cert_user_key_${ktype} \
++		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++		if [ $? -eq 0 ]; then
++			fail "ssh cert connect succeeded unexpectedly"
++		fi
++
++		# Correct authorized_principals
++		verbose "$tid: ${_prefix} correct authorized_principals"
++		echo mekmitasdigoat > $OBJ/authorized_principals_$USER
++		${SSH} -2i $OBJ/cert_user_key_${ktype} \
++		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++		if [ $? -ne 0 ]; then
++			fail "ssh cert connect failed"
++		fi
++
++		# Setup for principals= key option
++		rm -f $OBJ/authorized_principals_$USER
++		(
++			cat $OBJ/sshd_proxy_bak
++			echo "UsePrivilegeSeparation $privsep"
++		) > $OBJ/sshd_proxy
++
++		# Wrong principals list
++		verbose "$tid: ${_prefix} wrong principals key option"
++		(
++			echon 'cert-authority,principals="gregorsamsa" '
++			cat $OBJ/user_ca_key.pub
++		) > $OBJ/authorized_keys_$USER
++		${SSH} -2i $OBJ/cert_user_key_${ktype} \
++		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++		if [ $? -eq 0 ]; then
++			fail "ssh cert connect succeeded unexpectedly"
++		fi
++
++		# Correct principals list
++		verbose "$tid: ${_prefix} correct principals key option"
++		(
++			echon 'cert-authority,principals="mekmitasdigoat" '
++			cat $OBJ/user_ca_key.pub
++		) > $OBJ/authorized_keys_$USER
++		${SSH} -2i $OBJ/cert_user_key_${ktype} \
++		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++		if [ $? -ne 0 ]; then
++			fail "ssh cert connect failed"
++		fi
++	done
++done
++
++basic_tests() {
++	auth=$1
++	if test "x$auth" = "xauthorized_keys" ; then
++		# Add CA to authorized_keys
++		(
++			echon 'cert-authority '
++			cat $OBJ/user_ca_key.pub
++		) > $OBJ/authorized_keys_$USER
++	else
++		echo > $OBJ/authorized_keys_$USER
++		extra_sshd="TrustedUserCAKeys $OBJ/user_ca_key.pub"
++	fi
++	
++	for ktype in rsa dsa rsa_v00 dsa_v00 ; do 
++		for privsep in yes no ; do
++			_prefix="${ktype} privsep $privsep $auth"
++			# Simple connect
++			verbose "$tid: ${_prefix} connect"
++			(
++				cat $OBJ/sshd_proxy_bak
++				echo "UsePrivilegeSeparation $privsep"
++				echo "$extra_sshd"
++			) > $OBJ/sshd_proxy
++	
++			${SSH} -2i $OBJ/cert_user_key_${ktype} \
++			    -F $OBJ/ssh_proxy somehost true
++			if [ $? -ne 0 ]; then
++				fail "ssh cert connect failed"
++			fi
++
++			# Revoked keys
++			verbose "$tid: ${_prefix} revoked key"
++			(
++				cat $OBJ/sshd_proxy_bak
++				echo "UsePrivilegeSeparation $privsep"
++				echo "RevokedKeys $OBJ/cert_user_key_${ktype}.pub"
++				echo "$extra_sshd"
++			) > $OBJ/sshd_proxy
++			${SSH} -2i $OBJ/cert_user_key_${ktype} \
++			    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++			if [ $? -eq 0 ]; then
++				fail "ssh cert connect succeeded unexpecedly"
++			fi
++		done
++	
++		# Revoked CA
++		verbose "$tid: ${ktype} $auth revoked CA key"
++		(
++			cat $OBJ/sshd_proxy_bak
++			echo "RevokedKeys $OBJ/user_ca_key.pub"
++			echo "$extra_sshd"
++		) > $OBJ/sshd_proxy
++		${SSH} -2i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \
++		    somehost true >/dev/null 2>&1
++		if [ $? -eq 0 ]; then
++			fail "ssh cert connect succeeded unexpecedly"
++		fi
++	done
++	
++	verbose "$tid: $auth CA does not authenticate"
++	(
++		cat $OBJ/sshd_proxy_bak
++		echo "$extra_sshd"
++	) > $OBJ/sshd_proxy
++	verbose "$tid: ensure CA key does not authenticate user"
++	${SSH} -2i $OBJ/user_ca_key \
++	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++	if [ $? -eq 0 ]; then
++		fail "ssh cert connect with CA key succeeded unexpectedly"
++	fi
++}
++
++basic_tests authorized_keys
++basic_tests TrustedUserCAKeys
++
++test_one() {
++	ident=$1
++	result=$2
++	sign_opts=$3
++	auth_choice=$4
++	auth_opt=$5
++
++	if test "x$auth_choice" = "x" ; then
++		auth_choice="authorized_keys TrustedUserCAKeys"
++	fi
++
++	for auth in $auth_choice ; do
++		for ktype in rsa rsa_v00 ; do
++			cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy
++			if test "x$auth" = "xauthorized_keys" ; then
++				# Add CA to authorized_keys
++				(
++					echon "cert-authority${auth_opt} "
++					cat $OBJ/user_ca_key.pub
++				) > $OBJ/authorized_keys_$USER
++			else
++				echo > $OBJ/authorized_keys_$USER
++				echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" \
++				    >> $OBJ/sshd_proxy
++				if test "x$auth_opt" != "x" ; then
++					echo $auth_opt >> $OBJ/sshd_proxy
++				fi
++			fi
++			
++			verbose "$tid: $ident auth $auth expect $result $ktype"
++			${SSHKEYGEN} -q -s $OBJ/user_ca_key \
++			    -I "regress user key for $USER" \
++			    $sign_opts \
++			    $OBJ/cert_user_key_${ktype} ||
++				fail "couldn't sign cert_user_key_${ktype}"
++
++			${SSH} -2i $OBJ/cert_user_key_${ktype} \
++			    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
++			rc=$?
++			if [ "x$result" = "xsuccess" ] ; then
++				if [ $rc -ne 0 ]; then
++					fail "$ident failed unexpectedly"
++				fi
++			else
++				if [ $rc -eq 0 ]; then
++					fail "$ident succeeded unexpectedly"
++				fi
++			fi
++		done
++	done
++}
++
++test_one "correct principal"	success "-Z ${USER}"
++test_one "host-certificate"	failure "-Z ${USER} -h"
++test_one "wrong principals"	failure "-Z foo"
++test_one "cert not yet valid"	failure "-Z ${USER} -V20200101:20300101"
++test_one "cert expired"		failure "-Z ${USER} -V19800101:19900101"
++test_one "cert valid interval"	success "-Z ${USER} -V-1w:+2w"
++test_one "wrong source-address"	failure "-Z ${USER} -Osource-address=10.0.0.0/8"
++test_one "force-command"	failure "-Z ${USER} -Oforce-command=false"
++
++# Behaviour is different here: TrustedUserCAKeys doesn't allow empty principals
++test_one "empty principals"	success "" authorized_keys
++test_one "empty principals"	failure "" TrustedUserCAKeys
++
++# Check explicitly-specified principals: an empty principals list in the cert
++# should always be refused.
++
++# AuthorizedPrincipalsFile
++rm -f $OBJ/authorized_keys_$USER
++echo mekmitasdigoat > $OBJ/authorized_principals_$USER
++test_one "AuthorizedPrincipalsFile principals" success "-Z mekmitasdigoat" \
++    TrustedUserCAKeys "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u"
++test_one "AuthorizedPrincipalsFile no principals" failure "" \
++    TrustedUserCAKeys "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u"
++
++# principals= key option
++rm -f $OBJ/authorized_principals_$USER
++test_one "principals key option principals" success "-Z mekmitasdigoat" \
++    authorized_keys ',principals="mekmitasdigoat"'
++test_one "principals key option no principals" failure "" \
++    authorized_keys ',principals="mekmitasdigoat"'
++
++# Wrong certificate
++cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy
++for ktype in rsa dsa rsa_v00 dsa_v00 ; do 
++	case $ktype in
++	*_v00) args="-t v00" ;;
++	*) args="" ;;
++	esac
++	# Self-sign
++	${SSHKEYGEN} $args -q -s $OBJ/cert_user_key_${ktype} -I \
++	    "regress user key for $USER" \
++	    -Z $USER $OBJ/cert_user_key_${ktype} ||
++		fail "couldn't sign cert_user_key_${ktype}"
++	verbose "$tid: user ${ktype} connect wrong cert"
++	${SSH} -2i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \
++	    somehost true >/dev/null 2>&1
++	if [ $? -eq 0 ]; then
++		fail "ssh cert connect $ident succeeded unexpectedly"
++	fi
++done
++
++rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/cert_user_key*
++rm -f $OBJ/authorized_principals_$USER
++
+diff -up openssh-5.3p1/regress/Makefile.certificates openssh-5.3p1/regress/Makefile
+--- openssh-5.3p1/regress/Makefile.certificates	2008-06-30 00:07:58.000000000 +0200
++++ openssh-5.3p1/regress/Makefile	2013-07-18 15:38:20.440186394 +0200
+@@ -50,7 +50,9 @@ LTESTS= 	connect \
+ 		cfgmatch \
+ 		addrmatch \
+ 		localcommand \
+-		forcecommand
++		forcecommand \
++		cert-hostkey \
++		cert-userkey
+ 
+ INTEROP_TESTS=	putty-transfer putty-ciphers putty-kex conch-ciphers
+ #INTEROP_TESTS+=ssh-com ssh-com-client ssh-com-keygen ssh-com-sftp
+@@ -64,7 +66,9 @@ CLEANFILES=	t2.out t6.out1 t6.out2 t7.ou
+ 		ls.copy banner.in banner.out empty.in \
+ 		scp-ssh-wrapper.scp ssh_proxy_envpass remote_pid \
+ 		sshd_proxy_bak rsa_ssh2_cr.prv rsa_ssh2_crnl.prv \
+-		putty.rsa2
++		known_hosts-cert host_ca_key* cert_host_key* \
++		putty.rsa2 \
++		authorized_principals_${USER}
+ 
+ t1:
+ 	ssh-keygen -if ${.CURDIR}/rsa_ssh2.prv | diff - ${.CURDIR}/rsa_openssh.prv
+diff -up openssh-5.3p1/servconf.c.certificates openssh-5.3p1/servconf.c
+--- openssh-5.3p1/servconf.c.certificates	2013-07-18 15:38:20.417186494 +0200
++++ openssh-5.3p1/servconf.c	2013-07-18 15:39:27.258895162 +0200
+@@ -67,6 +67,7 @@ initialize_server_options(ServerOptions
+ 	options->listen_addrs = NULL;
+ 	options->address_family = -1;
+ 	options->num_host_key_files = 0;
++	options->num_host_cert_files = 0;
+ 	options->pid_file = NULL;
+ 	options->server_key_bits = -1;
+ 	options->login_grace_time = -1;
+@@ -139,6 +140,9 @@ initialize_server_options(ServerOptions
+ 	options->authorized_keys_command = NULL;
+ 	options->authorized_keys_command_runas = NULL;
+ 	options->zero_knowledge_password_authentication = -1;
++	options->revoked_keys_file = NULL;
++	options->trusted_user_ca_keys = NULL;
++	options->authorized_principals_file = NULL;
+ 	options->use_kuserok = -1;
+ }
+ 
+@@ -164,6 +168,7 @@ fill_default_server_options(ServerOption
+ 			    _PATH_HOST_DSA_KEY_FILE;
+ 		}
+ 	}
++	/* No certificates by default */
+ 	if (options->num_ports == 0)
+ 		options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
+ 	if (options->listen_addrs == NULL)
+@@ -331,7 +336,8 @@ typedef enum {
+ 	sRequiredAuthentications1, sRequiredAuthentications2,
+ 	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
+ 	sUsePrivilegeSeparation, sAllowAgentForwarding,
+-	sZeroKnowledgePasswordAuthentication,
++	sZeroKnowledgePasswordAuthentication, sHostCertificate,
++	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
+ 	sAuthorizedKeysCommand, sAuthorizedKeysCommandRunAs,
+ 	sKexAlgorithms,
+ 	sDeprecated, sUnsupported
+@@ -461,6 +467,10 @@ static struct {
+ 	{ "permitopen", sPermitOpen, SSHCFG_ALL },
+ 	{ "forcecommand", sForceCommand, SSHCFG_ALL },
+ 	{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
++	{ "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
++	{ "revokedkeys", sRevokedKeys, SSHCFG_ALL },
++	{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
++	{ "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_GLOBAL },
+ #ifdef WITH_AUTHORIZED_KEYS_COMMAND
+ 	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
+ 	{ "authorizedkeyscommandrunas", sAuthorizedKeysCommandRunAs, SSHCFG_ALL },
+@@ -506,6 +516,22 @@ parse_token(const char *cp, const char *
+ 	return sBadOption;
+ }
+ 
++char *
++derelativise_path(const char *path)
++{
++	char *expanded, *ret, *cwd;
++
++	expanded = tilde_expand_filename(path, getuid());
++	if (*expanded == '/')
++		return expanded;
++	if ((cwd = getcwd(NULL, 0)) == NULL)
++		fatal("%s: getcwd: %s", __func__, strerror(errno));
++	xasprintf(&ret, "%s/%s", cwd, expanded);
++	xfree(cwd);
++	xfree(expanded);
++	return ret;
++}
++
+ static void
+ add_listen_addr(ServerOptions *options, char *addr, int port)
+ {
+@@ -847,6 +873,16 @@ process_server_config_line(ServerOptions
+ 		}
+ 		break;
+ 
++	case sHostCertificate:
++		intptr = &options->num_host_cert_files;
++		if (*intptr >= MAX_HOSTKEYS)
++			fatal("%s line %d: too many host certificates "
++			    "specified (max %d).", filename, linenum,
++			    MAX_HOSTCERTS);
++		charptr = &options->host_cert_files[*intptr];
++		goto parse_filename;
++		break;
++
+ 	case sPidFile:
+ 		charptr = &options->pid_file;
+ 		goto parse_filename;
+@@ -1291,10 +1327,14 @@ process_server_config_line(ServerOptions
+ 	 * AuthorizedKeysFile	/etc/ssh_keys/%u
+ 	 */
+ 	case sAuthorizedKeysFile:
++		charptr = &options->authorized_keys_file;
++		goto parse_tilde_filename;
+ 	case sAuthorizedKeysFile2:
+-		charptr = (opcode == sAuthorizedKeysFile) ?
+-		    &options->authorized_keys_file :
+-		    &options->authorized_keys_file2;
++		charptr = &options->authorized_keys_file2;
++		goto parse_tilde_filename;
++	case sAuthorizedPrincipalsFile:
++		charptr = &options->authorized_principals_file;
++ parse_tilde_filename:
+ 		arg = strdelim(&cp);
+ 		if (!arg || *arg == '\0')
+ 			fatal("%s line %d: missing file name.",
+@@ -1428,6 +1468,14 @@ process_server_config_line(ServerOptions
+ 			*charptr = xstrdup(arg);
+ 		break;
+ 
++	case sTrustedUserCAKeys:
++		charptr = &options->trusted_user_ca_keys;
++		goto parse_filename;
++
++	case sRevokedKeys:
++		charptr = &options->revoked_keys_file;
++		goto parse_filename;
++
+ 	case sDeprecated:
+ 		logit("%s line %d: Deprecated option %s",
+ 		    filename, linenum, arg);
+@@ -1548,6 +1596,8 @@ copy_set_server_options(ServerOptions *d
+ 		return;
+ 	M_CP_STROPT(adm_forced_command);
+ 	M_CP_STROPT(chroot_directory);
++	M_CP_STROPT(trusted_user_ca_keys);
++	M_CP_STROPT(revoked_keys_file);
+ }
+ 
+ #undef M_CP_INTOPT
+@@ -1768,6 +1818,11 @@ dump_config(ServerOptions *o)
+ 	dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file);
+ 	dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2);
+ 	dump_cfg_string(sForceCommand, o->adm_forced_command);
++	dump_cfg_string(sChrootDirectory, o->chroot_directory);
++	dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys);
++	dump_cfg_string(sRevokedKeys, o->revoked_keys_file);
++	dump_cfg_string(sAuthorizedPrincipalsFile,
++	    o->authorized_principals_file);
+ 	dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
+ 	dump_cfg_string(sAuthorizedKeysCommandRunAs, o->authorized_keys_command_runas);
+ 
+@@ -1778,6 +1833,8 @@ dump_config(ServerOptions *o)
+ 	/* string array arguments */
+ 	dump_cfg_strarray(sHostKeyFile, o->num_host_key_files,
+ 	     o->host_key_files);
++	dump_cfg_strarray(sHostKeyFile, o->num_host_cert_files,
++	     o->host_cert_files);
+ 	dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users);
+ 	dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users);
+ 	dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups);
+diff -up openssh-5.3p1/servconf.h.certificates openssh-5.3p1/servconf.h
+--- openssh-5.3p1/servconf.h.certificates	2013-07-18 15:38:20.418186490 +0200
++++ openssh-5.3p1/servconf.h	2013-07-18 15:38:20.441186390 +0200
+@@ -24,6 +24,7 @@
+ #define MAX_DENY_GROUPS		256	/* Max # groups on deny list. */
+ #define MAX_SUBSYSTEMS		256	/* Max # subsystems. */
+ #define MAX_HOSTKEYS		256	/* Max # hostkeys. */
++#define MAX_HOSTCERTS		256	/* Max # host certificates. */
+ #define MAX_ACCEPT_ENV		256	/* Max # of env vars. */
+ #define MAX_MATCH_GROUPS	256	/* Max # of groups for Match. */
+ 
+@@ -49,6 +50,8 @@ typedef struct {
+ 	int     address_family;		/* Address family used by the server. */
+ 	char   *host_key_files[MAX_HOSTKEYS];	/* Files containing host keys. */
+ 	int     num_host_key_files;     /* Number of files for host keys. */
++	char   *host_cert_files[MAX_HOSTCERTS];	/* Files containing host certs. */
++	int     num_host_cert_files;     /* Number of files for host certs. */
+ 	char   *pid_file;	/* Where to put our pid */
+ 	int     server_key_bits;/* Size of the server key. */
+ 	int     login_grace_time;	/* Disconnect if no auth in this time
+@@ -160,6 +163,9 @@ typedef struct {
+ 	int	use_kuserok;
+ 
+ 	char   *chroot_directory;
++	char   *revoked_keys_file;
++	char   *trusted_user_ca_keys;
++	char   *authorized_principals_file;
+ 	char   *authorized_keys_command;
+ 	char   *authorized_keys_command_runas;
+ }       ServerOptions;
+@@ -175,5 +181,6 @@ void	 parse_server_match_config(ServerOp
+ 	     const char *);
+ void	 copy_set_server_options(ServerOptions *, ServerOptions *, int);
+ void	 dump_config(ServerOptions *);
++char   *derelativise_path(const char *);
+ 
+ #endif				/* SERVCONF_H */
+diff -up openssh-5.3p1/ssh.1.certificates openssh-5.3p1/ssh.1
+--- openssh-5.3p1/ssh.1.certificates	2013-07-18 15:38:20.422186472 +0200
++++ openssh-5.3p1/ssh.1	2013-07-18 15:38:20.441186390 +0200
+@@ -307,6 +307,11 @@ It is possible to have multiple
+ .Fl i
+ options (and multiple identities specified in
+ configuration files).
++.Nm
++will also try to load certificate information from the filename obtained
++by appending
++.Pa -cert.pub
++to identity filenames.
+ .It Fl K
+ Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI
+ credentials to the server.
+@@ -803,8 +808,20 @@ file, and has one key
+ per line, though the lines can be very long.
+ After this, the user can log in without giving the password.
+ .Pp
+-The most convenient way to use public key authentication may be with an
+-authentication agent.
++A variation on public key authentication
++is available in the form of certificate authentication:
++instead of a set of public/private keys,
++signed certificates are used.
++This has the advantage that a single trusted certification authority
++can be used in place of many public/private keys.
++See the
++.Sx CERTIFICATES
++section of
++.Xr ssh-keygen 1
++for more information.
++.Pp
++The most convenient way to use public key or certificate authentication
++may be with an authentication agent.
+ See
+ .Xr ssh-agent 1
+ for more information.
+diff -up openssh-5.3p1/ssh.1.netcat.certificates openssh-5.3p1/ssh.1.netcat
+diff -up openssh-5.3p1/ssh2.h.certificates openssh-5.3p1/ssh2.h
+--- openssh-5.3p1/ssh2.h.certificates	2008-11-05 06:20:47.000000000 +0100
++++ openssh-5.3p1/ssh2.h	2013-07-18 15:38:20.442186385 +0200
+@@ -166,3 +166,6 @@
+ 
+ #define SSH2_EXTENDED_DATA_STDERR			1
+ 
++/* Certificate types for OpenSSH certificate keys extension */
++#define SSH2_CERT_TYPE_USER				1
++#define SSH2_CERT_TYPE_HOST				2
+diff -up openssh-5.3p1/ssh-add.1.certificates openssh-5.3p1/ssh-add.1
+--- openssh-5.3p1/ssh-add.1.certificates	2013-07-18 15:38:20.410186525 +0200
++++ openssh-5.3p1/ssh-add.1	2013-07-18 15:38:20.442186385 +0200
+@@ -64,7 +64,14 @@ When run without arguments, it adds the
+ .Pa ~/.ssh/id_dsa
+ and
+ .Pa ~/.ssh/identity .
++After loading a private key,
++.Nm
++will try to load corresponding certificate information from the
++filename obtained by appending
++.Pa -cert.pub
++to the name of the private key file.
+ Alternative file names can be given on the command line.
++.Pp
+ If any file requires a passphrase,
+ .Nm
+ asks for the passphrase from the user.
+diff -up openssh-5.3p1/ssh-add.c.certificates openssh-5.3p1/ssh-add.c
+--- openssh-5.3p1/ssh-add.c.certificates	2013-07-18 15:38:20.411186520 +0200
++++ openssh-5.3p1/ssh-add.c	2013-07-18 15:38:20.442186385 +0200
+@@ -148,9 +148,9 @@ delete_all(AuthenticationConnection *ac)
+ static int
+ add_file(AuthenticationConnection *ac, const char *filename)
+ {
+-	Key *private;
++	Key *private, *cert;
+ 	char *comment = NULL;
+-	char msg[1024];
++	char msg[1024], *certpath;
+ 	int fd, perms_ok, ret = -1;
+ 
+ 	if ((fd = open(filename, O_RDONLY)) < 0) {
+@@ -204,7 +204,7 @@ add_file(AuthenticationConnection *ac, c
+ 			    "Lifetime set to %d seconds\n", lifetime);
+ 		if (confirm != 0)
+ 			fprintf(stderr,
+-			    "The user has to confirm each use of the key\n");
++			    "The user must confirm each use of the key\n");
+ 	} else if (ssh_add_identity(ac, private, comment)) {
+ 		fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
+ 		ret = 0;
+@@ -212,6 +212,41 @@ add_file(AuthenticationConnection *ac, c
+ 		fprintf(stderr, "Could not add identity: %s\n", filename);
+ 	}
+ 
++
++	/* Now try to add the certificate flavour too */
++	xasprintf(&certpath, "%s-cert.pub", filename);
++	if ((cert = key_load_public(certpath, NULL)) == NULL)
++		goto out;
++
++	if (!key_equal_public(cert, private)) {
++		error("Certificate %s does not match private key %s",
++		    certpath, filename);
++		key_free(cert);
++		goto out;
++	}
++
++	/* Graft with private bits */
++	if (key_to_certified(private, key_cert_is_legacy(cert)) != 0) {
++		error("%s: key_to_certified failed", __func__);
++		key_free(cert);
++		goto out;
++	}
++	key_cert_copy(cert, private);
++	key_free(cert);
++
++	if (!ssh_add_identity_constrained(ac, private, comment,
++	    lifetime, confirm)) {
++		error("Certificate %s (%s) add failed", certpath,
++		    private->cert->key_id);
++	}
++	fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
++	    private->cert->key_id);
++	if (lifetime != 0)
++		fprintf(stderr, "Lifetime set to %d seconds\n", lifetime);
++	if (confirm != 0)
++		fprintf(stderr, "The user must confirm each use of the key\n");
++ out:
++	xfree(certpath);
+ 	xfree(comment);
+ 	key_free(private);
+ 
+diff -up openssh-5.3p1/ssh-agent.c.certificates openssh-5.3p1/ssh-agent.c
+--- openssh-5.3p1/ssh-agent.c.certificates	2013-07-18 15:38:20.411186520 +0200
++++ openssh-5.3p1/ssh-agent.c	2013-07-18 15:38:20.443186381 +0200
+@@ -473,6 +473,8 @@ process_add_identity(SocketEntry *e, int
+ 	int type, success = 0, death = 0, confirm = 0;
+ 	char *type_name, *comment;
+ 	Key *k = NULL;
++	u_char *cert;
++	u_int len;
+ 
+ 	switch (version) {
+ 	case 1:
+@@ -503,6 +505,15 @@ process_add_identity(SocketEntry *e, int
+ 			buffer_get_bignum2(&e->request, k->dsa->pub_key);
+ 			buffer_get_bignum2(&e->request, k->dsa->priv_key);
+ 			break;
++		case KEY_DSA_CERT_V00:
++		case KEY_DSA_CERT:
++			cert = buffer_get_string(&e->request, &len);
++			if ((k = key_from_blob(cert, len)) == NULL)
++				fatal("Certificate parse failed");
++			xfree(cert);
++			key_add_private(k);
++			buffer_get_bignum2(&e->request, k->dsa->priv_key);
++			break;
+ 		case KEY_RSA:
+ 			k = key_new_private(type);
+ 			buffer_get_bignum2(&e->request, k->rsa->n);
+@@ -515,6 +526,18 @@ process_add_identity(SocketEntry *e, int
+ 			/* Generate additional parameters */
+ 			rsa_generate_additional_parameters(k->rsa);
+ 			break;
++		case KEY_RSA_CERT_V00:
++		case KEY_RSA_CERT:
++			cert = buffer_get_string(&e->request, &len);
++			if ((k = key_from_blob(cert, len)) == NULL)
++				fatal("Certificate parse failed");
++			xfree(cert);
++			key_add_private(k);
++			buffer_get_bignum2(&e->request, k->rsa->d);
++			buffer_get_bignum2(&e->request, k->rsa->iqmp);
++			buffer_get_bignum2(&e->request, k->rsa->p);
++			buffer_get_bignum2(&e->request, k->rsa->q);
++			break;
+ 		default:
+ 			buffer_clear(&e->request);
+ 			goto send;
+@@ -524,6 +547,8 @@ process_add_identity(SocketEntry *e, int
+ 	/* enable blinding */
+ 	switch (k->type) {
+ 	case KEY_RSA:
++	case KEY_RSA_CERT_V00:
++	case KEY_RSA_CERT:
+ 	case KEY_RSA1:
+ 		if (RSA_blinding_on(k->rsa, NULL) != 1) {
+ 			error("process_add_identity: RSA_blinding_on failed");
+diff -up openssh-5.3p1/ssh_config.5.certificates openssh-5.3p1/ssh_config.5
+--- openssh-5.3p1/ssh_config.5.certificates	2013-07-18 15:38:20.423186468 +0200
++++ openssh-5.3p1/ssh_config.5	2013-07-18 15:38:20.444186376 +0200
+@@ -586,6 +586,12 @@ and
+ for protocol version 2.
+ Additionally, any identities represented by the authentication agent
+ will be used for authentication.
++.Xr ssh 1
++will try to load certificate information from the filename obtained by
++appending
++.Pa -cert.pub
++to the path of a specified
++.Cm IdentityFile .
+ .Pp
+ The file name may use the tilde
+ syntax to refer to a user's home directory or one of the following
+diff -up openssh-5.3p1/sshconnect2.c.certificates openssh-5.3p1/sshconnect2.c
+--- openssh-5.3p1/sshconnect2.c.certificates	2013-07-18 15:38:20.418186490 +0200
++++ openssh-5.3p1/sshconnect2.c	2013-07-18 15:38:20.445186372 +0200
+@@ -1246,8 +1246,11 @@ sign_and_send_pubkey(Authctxt *authctxt,
+ 	u_int skip = 0;
+ 	int ret = -1;
+ 	int have_sig = 1;
++	char *fp;
+ 
+-	debug3("sign_and_send_pubkey");
++	fp = key_fingerprint(id->key, SSH_FP_SHA1, SSH_FP_HEX);
++	debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp);
++	xfree(fp);
+ 
+ 	if (key_to_blob(id->key, &blob, &bloblen) == 0) {
+ 		/* we cannot handle this key */
+@@ -1417,6 +1420,8 @@ pubkey_prepare(Authctxt *authctxt)
+ 		key = options.identity_keys[i];
+ 		if (key && key->type == KEY_RSA1)
+ 			continue;
++		if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER)
++			continue;
+ 		options.identity_keys[i] = NULL;
+ 		id = xcalloc(1, sizeof(*id));
+ 		id->key = key;
+diff -up openssh-5.3p1/sshconnect.c.certificates openssh-5.3p1/sshconnect.c
+--- openssh-5.3p1/sshconnect.c.certificates	2013-07-18 15:38:20.294187030 +0200
++++ openssh-5.3p1/sshconnect.c	2013-07-18 15:38:20.445186372 +0200
+@@ -60,6 +60,7 @@
+ #include "misc.h"
+ #include "dns.h"
+ #include "roaming.h"
++#include "ssh2.h"
+ #include "version.h"
+ 
+ char *client_version_string = NULL;
+@@ -578,6 +579,23 @@ confirm(const char *prompt)
+ 	}
+ }
+ 
++static int
++check_host_cert(const char *host, const Key *host_key)
++{
++	const char *reason;
++
++	if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) {
++		error("%s", reason);
++		return 0;
++	}
++	if (buffer_len(&host_key->cert->critical) != 0) {
++		error("Certificate for %s contains unsupported "
++		    "critical options(s)", host);
++		return 0;
++	}
++	return 1;
++}
++
+ /*
+  * check whether the supplied host key is valid, return -1 if the key
+  * is not valid. the user_hostfile will not be updated if 'readonly' is true.
+@@ -590,13 +608,13 @@ check_host_key(char *hostname, struct so
+     Key *host_key, int readonly, const char *user_hostfile,
+     const char *system_hostfile)
+ {
+-	Key *file_key;
+-	const char *type = key_type(host_key);
++	Key *file_key, *raw_key = NULL;
++	const char *type;
+ 	char *ip = NULL, *host = NULL;
+ 	char hostline[1000], *hostp, *fp, *ra;
+ 	HostStatus host_status;
+ 	HostStatus ip_status;
+-	int r, local = 0, host_ip_differ = 0;
++	int r, want_cert, local = 0, host_ip_differ = 0;
+ 	int salen;
+ 	char ntop[NI_MAXHOST];
+ 	char msg[1024];
+@@ -669,11 +687,15 @@ check_host_key(char *hostname, struct so
+ 		host = put_host_port(hostname, port);
+ 	}
+ 
++ retry:
++	want_cert = key_is_cert(host_key);
++	type = key_type(host_key);
++
+ 	/*
+ 	 * Store the host key from the known host file in here so that we can
+ 	 * compare it with the key for the IP address.
+ 	 */
+-	file_key = key_new(host_key->type);
++	file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type);
+ 
+ 	/*
+ 	 * Check if the host key is present in the user's list of known
+@@ -689,9 +711,10 @@ check_host_key(char *hostname, struct so
+ 	}
+ 	/*
+ 	 * Also perform check for the ip address, skip the check if we are
+-	 * localhost or the hostname was an ip address to begin with
++	 * localhost, looking for a certificate, or the hostname was an ip
++	 * address to begin with.
+ 	 */
+-	if (options.check_host_ip) {
++	if (!want_cert && options.check_host_ip) {
+ 		Key *ip_key = key_new(host_key->type);
+ 
+ 		ip_file = user_hostfile;
+@@ -715,11 +738,14 @@ check_host_key(char *hostname, struct so
+ 	switch (host_status) {
+ 	case HOST_OK:
+ 		/* The host is known and the key matches. */
+-		debug("Host '%.200s' is known and matches the %s host key.",
+-		    host, type);
+-		debug("Found key in %s:%d", host_file, host_line);
++		debug("Host '%.200s' is known and matches the %s host %s.",
++		    host, type, want_cert ? "certificate" : "key");
++		debug("Found %s in %s:%d",
++		    want_cert ? "CA key" : "key", host_file, host_line);
++		if (want_cert && !check_host_cert(hostname, host_key))
++			goto fail;
+ 		if (options.check_host_ip && ip_status == HOST_NEW) {
+-			if (readonly)
++			if (readonly || want_cert)
+ 				logit("%s host key for IP address "
+ 				    "'%.128s' not in list of known hosts.",
+ 				    type, ip);
+@@ -733,8 +759,9 @@ check_host_key(char *hostname, struct so
+ 				    "key for IP address '%.128s' to the list "
+ 				    "of known hosts.", type, ip);
+ 		} else if (options.visual_host_key) {
+-			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
+-			ra = key_fingerprint(host_key, SSH_FP_MD5,
++			int fips_on = FIPS_mode();
++			fp = key_fingerprint(host_key, fips_on ? SSH_FP_SHA1 : SSH_FP_MD5, SSH_FP_HEX);
++			ra = key_fingerprint(host_key, fips_on ? SSH_FP_SHA1 : SSH_FP_MD5,
+ 			    SSH_FP_RANDOMART);
+ 			logit("Host key fingerprint is %s\n%s\n", fp, ra);
+ 			xfree(ra);
+@@ -751,7 +778,7 @@ check_host_key(char *hostname, struct so
+ 				break;
+ 			}
+ 		}
+-		if (readonly)
++		if (readonly || want_cert)
+ 			goto fail;
+ 		/* The host is new. */
+ 		if (options.strict_host_key_checking == 1) {
+@@ -836,7 +863,37 @@ check_host_key(char *hostname, struct so
+ 			logit("Warning: Permanently added '%.200s' (%s) to the "
+ 			    "list of known hosts.", hostp, type);
+ 		break;
++	case HOST_REVOKED:
++		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
++		error("@       WARNING: REVOKED HOST KEY DETECTED!               @");
++		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
++		error("The %s host key for %s is marked as revoked.", type, host);
++		error("This could mean that a stolen key is being used to");
++		error("impersonate this host.");
++
++		/*
++		 * If strict host key checking is in use, the user will have
++		 * to edit the key manually and we can only abort.
++		 */
++		if (options.strict_host_key_checking) {
++			error("%s host key for %.200s was revoked and you have "
++			    "requested strict checking.", type, host);
++			goto fail;
++		}
++		goto continue_unsafe;
++
+ 	case HOST_CHANGED:
++		if (want_cert) {
++			/*
++			 * This is only a debug() since it is valid to have
++			 * CAs with wildcard DNS matches that don't match
++			 * all hosts that one might visit.
++			 */
++			debug("Host certificate authority does not "
++			    "match %s in %s:%d", CA_MARKER,
++			    host_file, host_line);
++			goto fail;
++		}
+ 		if (readonly == ROQUIET)
+ 			goto fail;
+ 		if (options.check_host_ip && host_ip_differ) {
+@@ -874,6 +931,7 @@ check_host_key(char *hostname, struct so
+ 			goto fail;
+ 		}
+ 
++ continue_unsafe:
+ 		/*
+ 		 * If strict host key checking has not been requested, allow
+ 		 * the connection but without MITM-able authentication or
+@@ -973,6 +1031,20 @@ check_host_key(char *hostname, struct so
+ 	return 0;
+ 
+ fail:
++	if (want_cert && host_status != HOST_REVOKED) {
++		/*
++		 * No matching certificate. Downgrade cert to raw key and
++		 * search normally.
++		 */
++		debug("No matching CA found. Retry with plain key");
++		raw_key = key_from_private(host_key);
++		if (key_drop_cert(raw_key) != 0)
++			fatal("Couldn't drop certificate");
++		host_key = raw_key;
++		goto retry;
++	}
++	if (raw_key != NULL)
++		key_free(raw_key);
+ 	xfree(ip);
+ 	xfree(host);
+ 	return -1;
+@@ -985,7 +1057,8 @@ verify_host_key(char *host, struct socka
+ 	struct stat st;
+ 	int flags = 0;
+ 
+-	if (options.verify_host_key_dns &&
++	/* XXX certs are not yet supported for DNS */
++	if (!key_is_cert(host_key) && options.verify_host_key_dns &&
+ 	    verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) {
+ 
+ 		if (flags & DNS_VERIFY_FOUND) {
+diff -up openssh-5.3p1/sshd.8.certificates openssh-5.3p1/sshd.8
+--- openssh-5.3p1/sshd.8.certificates	2013-07-18 15:38:20.423186468 +0200
++++ openssh-5.3p1/sshd.8	2013-07-18 15:38:20.446186368 +0200
+@@ -47,6 +47,7 @@
+ .Op Fl 46DdeiqTt
+ .Op Fl b Ar bits
+ .Op Fl C Ar connection_spec
++.Op Fl c Ar host_certificate_file
+ .Op Fl f Ar config_file
+ .Op Fl g Ar login_grace_time
+ .Op Fl h Ar host_key_file
+@@ -101,6 +102,15 @@ to use IPv6 addresses only.
+ .It Fl b Ar bits
+ Specifies the number of bits in the ephemeral protocol version 1
+ server key (default 1024).
++.It Fl c Ar host_certificate_file
++Specifies a path to a certificate file to identify
++.Nm
++during key exchange.
++The certificate file must match a host key file specified using the
++.Fl -h
++option or the
++.Cm HostKey
++configuration directive.
+ .It Fl C Ar connection_spec
+ Specify the connection parameters to use for the
+ .Fl T
+@@ -499,6 +509,13 @@ No spaces are permitted, except within d
+ The following option specifications are supported (note
+ that option keywords are case-insensitive):
+ .Bl -tag -width Ds
++.It Cm cert-authority
++Specifies that the listed key is a certification authority (CA) that is
++trusted to validate signed certificates for user authentication.
++.Pp
++Certificates may encode access restrictions similar to these key options.
++If both certificate restrictions and key options are present, the most
++restrictive union of the two is applied.
+ .It Cm command="command"
+ Specifies that the command is executed whenever this key is used for
+ authentication.
+@@ -518,6 +535,10 @@ The command originally supplied by the c
+ .Ev SSH_ORIGINAL_COMMAND
+ environment variable.
+ Note that this option applies to shell, command or subsystem execution.
++Also note that this command may be superseded by either a
++.Xr sshd_config 5
++.Cm ForceCommand
++directive or a command embedded in a certificate.
+ .It Cm environment="NAME=value"
+ Specifies that the string is to be added to the environment when
+ logging in using this key.
+@@ -582,6 +603,17 @@ Multiple
+ options may be applied separated by commas.
+ No pattern matching is performed on the specified hostnames,
+ they must be literal domains or addresses.
++.It Cm principals="principals"
++On a
++.Cm cert-authority
++line, specifies allowed principals for certificate authentication as a
++comma-separated list.
++At least one name from the list must appear in the certificate's
++list of principals for the certificate to be accepted.
++This option is ignored for keys that are not marked as trusted certificate
++signers using the
++.Cm cert-authority
++option.
+ .It Cm tunnel="n"
+ Force a
+ .Xr tun 4
+@@ -614,10 +646,19 @@ be prepared by the administrator (option
+ maintained automatically: whenever the user connects from an unknown host,
+ its key is added to the per-user file.
+ .Pp
+-Each line in these files contains the following fields: hostnames,
+-bits, exponent, modulus, comment.
++Each line in these files contains the following fields: markers (optional),
++hostnames, bits, exponent, modulus, comment.
+ The fields are separated by spaces.
+ .Pp
++The marker is optional, but if it is present then it must be one of
++.Dq @cert-authority ,
++to indicate that the line contains a certification authority (CA) key,
++or
++.Dq @revoked ,
++to indicate that the key contained on the line is revoked and must not ever
++be accepted.
++Only one marker should be used on a key line.
++.Pp
+ Hostnames is a comma-separated list of patterns
+ .Pf ( Ql *
+ and
+@@ -657,8 +698,25 @@ Lines starting with
+ and empty lines are ignored as comments.
+ .Pp
+ When performing host authentication, authentication is accepted if any
+-matching line has the proper key.
+-It is thus permissible (but not
++matching line has the proper key; either one that matches exactly or,
++if the server has presented a certificate for authentication, the key
++of the certification authority that signed the certificate.
++For a key to be trusted as a certification authority, it must use the
++.Dq @cert-authority
++marker described above.
++.Pp
++The known hosts file also provides a facility to mark keys as revoked,
++for example when it is known that the associated private key has been
++stolen.
++Revoked keys are specified by including the
++.Dq @revoked
++marker at the beginning of the key line, and are never accepted for
++authentication or as certification authorities, but instead will
++produce a warning from
++.Xr ssh 1
++when they are encountered.
++.Pp
++It is permissible (but not
+ recommended) to have several lines or different host keys for the same
+ names.
+ This will inevitably happen when short forms of host names
+@@ -669,10 +727,16 @@ accepted if valid information can be fou
+ .Pp
+ Note that the lines in these files are typically hundreds of characters
+ long, and you definitely don't want to type in the host keys by hand.
+-Rather, generate them by a script
++Rather, generate them by a script,
++.Xr ssh-keyscan 1
+ or by taking
+ .Pa /etc/ssh/ssh_host_key.pub
+ and adding the host names at the front.
++.Xr ssh-keygen 1
++also offers some basic automated editing for
++.Pa ~/.ssh/known_hosts
++including removing hosts matching a host name and converting all host
++names to their hashed representations.
+ .Pp
+ An example ssh_known_hosts file:
+ .Bd -literal -offset 3n
+@@ -682,6 +746,10 @@ cvs.example.net,192.0.2.10 ssh-rsa AAAA1
+ # A hashed hostname
+ |1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa
+ AAAA1234.....=
++# A revoked key
++ at revoked * ssh-rsa AAAAB5W...
++# A CA key, accepted for any host in *.mydomain.com or *.mydomain.org
++ at cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W...
+ .Ed
+ .Sh FILES
+ .Bl -tag -width Ds -compact
+diff -up openssh-5.3p1/sshd.c.certificates openssh-5.3p1/sshd.c
+--- openssh-5.3p1/sshd.c.certificates	2013-07-18 15:38:20.419186485 +0200
++++ openssh-5.3p1/sshd.c	2013-07-18 15:38:20.446186368 +0200
+@@ -212,6 +212,7 @@ struct {
+ 	Key	*server_key;		/* ephemeral server key */
+ 	Key	*ssh1_host_key;		/* ssh1 host key */
+ 	Key	**host_keys;		/* all private host keys */
++	Key	**host_certificates;	/* all public host certificates */
+ 	int	have_ssh1_key;
+ 	int	have_ssh2_key;
+ 	u_char	ssh1_cookie[SSH_SESSION_KEY_LENGTH];
+@@ -575,6 +576,7 @@ destroy_sensitive_data(int privsep)
+ 				fp = NULL;
+  			key_free(sensitive_data.host_keys[i]);
+  			sensitive_data.host_keys[i] = NULL;
++
+ 			if (fp != NULL) {
+ 				if (privsep)
+ 					PRIVSEP(audit_destroy_sensitive_data(fp,
+@@ -585,6 +587,10 @@ destroy_sensitive_data(int privsep)
+ 				xfree(fp);
+ 			}
+ 		}
++		if (sensitive_data.host_certificates[i]) {
++			key_free(sensitive_data.host_certificates[i]);
++			sensitive_data.host_certificates[i] = NULL;
++		}
+ 	}
+ 	sensitive_data.ssh1_host_key = NULL;
+ 	memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH);
+@@ -627,6 +633,7 @@ demote_sensitive_data(void)
+ 				xfree(fp);
+ 			}
+ 		}
++		/* Certs do not need demotion */
+ 	}
+ 
+ 	/* We do not clear ssh1_host key and cookie.  XXX - Okay Niels? */
+@@ -783,10 +790,11 @@ list_hostkey_types(void)
+ 	const char *p;
+ 	char *ret;
+ 	int i;
++	Key *key;
+ 
+ 	buffer_init(&b);
+ 	for (i = 0; i < options.num_host_key_files; i++) {
+-		Key *key = sensitive_data.host_keys[i];
++		key = sensitive_data.host_keys[i];
+ 		if (key == NULL)
+ 			continue;
+ 		switch (key->type) {
+@@ -798,6 +806,21 @@ list_hostkey_types(void)
+ 			buffer_append(&b, p, strlen(p));
+ 			break;
+ 		}
++		/* If the private key has a cert peer, then list that too */
++		key = sensitive_data.host_certificates[i];
++		if (key == NULL)
++			continue;
++		switch (key->type) {
++		case KEY_RSA_CERT_V00:
++		case KEY_DSA_CERT_V00:
++		case KEY_RSA_CERT:
++		case KEY_DSA_CERT:
++			if (buffer_len(&b) > 0)
++				buffer_append(&b, ",", 1);
++			p = key_ssh_name(key);
++			buffer_append(&b, p, strlen(p));
++			break;
++		}
+ 	}
+ 	buffer_append(&b, "\0", 1);
+ 	ret = xstrdup(buffer_ptr(&b));
+@@ -806,20 +829,44 @@ list_hostkey_types(void)
+ 	return ret;
+ }
+ 
+-Key *
+-get_hostkey_by_type(int type)
++static Key *
++get_hostkey_by_type(int type, int need_private)
+ {
+ 	int i;
++	Key *key;
+ 
+ 	for (i = 0; i < options.num_host_key_files; i++) {
+-		Key *key = sensitive_data.host_keys[i];
++		switch (type) {
++		case KEY_RSA_CERT_V00:
++		case KEY_DSA_CERT_V00:
++		case KEY_RSA_CERT:
++		case KEY_DSA_CERT:
++			key = sensitive_data.host_certificates[i];
++			break;
++		default:
++			key = sensitive_data.host_keys[i];
++			break;
++		}
+ 		if (key != NULL && key->type == type)
+-			return key;
++			return need_private ?
++			    sensitive_data.host_keys[i] : key;
+ 	}
+ 	return NULL;
+ }
+ 
+ Key *
++get_hostkey_public_by_type(int type)
++{
++	return get_hostkey_by_type(type, 0);
++}
++
++Key *
++get_hostkey_private_by_type(int type)
++{
++	return get_hostkey_by_type(type, 1);
++}
++
++Key *
+ get_hostkey_by_index(int ind)
+ {
+ 	if (ind < 0 || ind >= options.num_host_key_files)
+@@ -833,8 +880,13 @@ get_hostkey_index(Key *key)
+ 	int i;
+ 
+ 	for (i = 0; i < options.num_host_key_files; i++) {
+-		if (key == sensitive_data.host_keys[i])
+-			return (i);
++		if (key_is_cert(key)) {
++			if (key == sensitive_data.host_certificates[i])
++				return (i);
++		} else {
++			if (key == sensitive_data.host_keys[i])
++				return (i);
++		}
+ 	}
+ 	return (-1);
+ }
+@@ -873,9 +925,9 @@ usage(void)
+ 	fprintf(stderr, "%s, %s\n",
+ 	    SSH_RELEASE, SSLeay_version(SSLEAY_VERSION));
+ 	fprintf(stderr,
+-"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-f config_file]\n"
+-"            [-g login_grace_time] [-h host_key_file] [-k key_gen_time]\n"
+-"            [-o option] [-p port] [-u len]\n"
++"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n"
++"            [-f config_file] [-g login_grace_time] [-h host_key_file]\n"
++"            [-k key_gen_time] [-o option] [-p port] [-u len]\n"
+ 	);
+ 	exit(1);
+ }
+@@ -1309,7 +1361,7 @@ main(int ac, char **av)
+ {
+ 	extern char *optarg;
+ 	extern int optind;
+-	int opt, i, on = 1;
++	int opt, i, j, on = 1;
+ 	int sock_in = -1, sock_out = -1, newsock = -1;
+ 	const char *remote_ip;
+ 	char *test_user = NULL, *test_host = NULL, *test_addr = NULL;
+@@ -1368,6 +1420,14 @@ main(int ac, char **av)
+ 		case 'f':
+ 			config_file_name = optarg;
+ 			break;
++		case 'c':
++			if (options.num_host_cert_files >= MAX_HOSTCERTS) {
++				fprintf(stderr, "too many host certificates.\n");
++				exit(1);
++			}
++			options.host_cert_files[options.num_host_cert_files++] =
++			   derelativise_path(optarg);
++			break;
+ 		case 'd':
+ 			if (debug_flag == 0) {
+ 				debug_flag = 1;
+@@ -1620,6 +1680,46 @@ main(int ac, char **av)
+ 		exit(1);
+ 	}
+ 
++	/*
++	 * Load certificates. They are stored in an array at identical
++	 * indices to the public keys that they relate to.
++	 */
++	sensitive_data.host_certificates = xcalloc(options.num_host_key_files,
++	    sizeof(Key *));
++	for (i = 0; i < options.num_host_key_files; i++)
++		sensitive_data.host_certificates[i] = NULL;
++
++	for (i = 0; i < options.num_host_cert_files; i++) {
++		key = key_load_public(options.host_cert_files[i], NULL);
++		if (key == NULL) {
++			error("Could not load host certificate: %s",
++			    options.host_cert_files[i]);
++			continue;
++		}
++		if (!key_is_cert(key)) {
++			error("Certificate file is not a certificate: %s",
++			    options.host_cert_files[i]);
++			key_free(key);
++			continue;
++		}
++		/* Find matching private key */
++		for (j = 0; j < options.num_host_key_files; j++) {
++			if (key_equal_public(key,
++			    sensitive_data.host_keys[j])) {
++				sensitive_data.host_certificates[j] = key;
++				break;
++			}
++		}
++		if (j >= options.num_host_key_files) {
++			error("No matching private key for certificate: %s",
++			    options.host_cert_files[i]);
++			key_free(key);
++			continue;
++		}
++		sensitive_data.host_certificates[j] = key;
++		debug("host certificate: #%d type %d %s", j, key->type,
++		    key_type(key));
++	}
+ 	/* Check certain values for sanity. */
+ 	if (options.protocol & SSH_PROTO_1) {
+ 		if (options.server_key_bits < 512 ||
+@@ -2416,7 +2516,8 @@ do_ssh2_kex(void)
+ 	kex->server = 1;
+ 	kex->client_version_string=client_version_string;
+ 	kex->server_version_string=server_version_string;
+-	kex->load_host_key=&get_hostkey_by_type;
++	kex->load_host_public_key=&get_hostkey_public_by_type;
++	kex->load_host_private_key=&get_hostkey_private_by_type;
+ 	kex->host_key_index=&get_hostkey_index;
+ 
+ 	xxx_kex = kex;
+diff -up openssh-5.3p1/sshd_config.5.certificates openssh-5.3p1/sshd_config.5
+--- openssh-5.3p1/sshd_config.5.certificates	2013-07-18 15:38:20.424186464 +0200
++++ openssh-5.3p1/sshd_config.5	2013-07-18 15:38:20.447186363 +0200
+@@ -167,6 +167,42 @@ is taken to be an absolute path or one r
+ directory.
+ The default is
+ .Dq .ssh/authorized_keys .
++.It Cm AuthorizedPrincipalsFile
++Specifies a file that lists principal names that are accepted for
++certificate authentication.
++When using certificates signed by a key listed in
++.Cm TrustedUserCAKeys ,
++this file lists names, one of which must appear in the certificate for it
++to be accepted for authentication.
++Names are listed one per line; empty lines and comments starting with
++.Ql #
++are ignored.
++.Pp
++.Cm AuthorizedPrincipalsFile
++may contain tokens of the form %T which are substituted during connection
++setup.
++The following tokens are defined: %% is replaced by a literal '%',
++%h is replaced by the home directory of the user being authenticated, and
++%u is replaced by the username of that user.
++After expansion,
++.Cm AuthorizedPrincipalsFile
++is taken to be an absolute path or one relative to the user's home
++directory.
++.Pp
++The default is not to use a principals file \(en in this case, the username
++of the user must appear in a certificate's principals list for it to be
++accepted.
++Note that
++.Cm AuthorizedPrincipalsFile
++is only used when authentication proceeds using a CA listed in
++.Cm TrustedUserCAKeys
++and is not consulted for certification authorities trusted via
++.Pa ~/.ssh/authorized_keys ,
++though the
++.Cm principals=
++key option offers a similar facility (see
++.Xr sshd 8
++for details).
+ .It Cm Banner
+ The contents of the specified file are sent to the remote user before
+ authentication is allowed.
+@@ -439,6 +475,14 @@ uses the name supplied by the client rat
+ attempting to resolve the name from the TCP connection itself.
+ The default is
+ .Dq no .
++.It Cm HostCertificate
++Specifies a file containing a public host certificate.
++The certificate's public key must match a private host key already specified
++by
++.Cm HostKey .
++The default behaviour of
++.Xr sshd 8
++is not to load any certificates.
+ .It Cm HostKey
+ Specifies a file containing a private host key
+ used by SSH.
+@@ -887,6 +931,11 @@ Available methods:
+ .Bd -literal -offset 3n
+ password, keyboard-interactive, publickey, hostbased, gssapi-keyex, gssapi-with-mic
+ .Ed
++.It Cm RevokedKeys
++Specifies a list of revoked public keys.
++Keys listed in this file will be refused for public key authentication.
++Note that if this file is not readable, then public key authentication will
++be refused for all users.
+ .It Cm RhostsRSAAuthentication
+ Specifies whether rhosts or /etc/hosts.equiv authentication together
+ with successful RSA host authentication is allowed.
+@@ -967,6 +1016,22 @@ This avoids infinitely hanging sessions.
+ .Pp
+ To disable TCP keepalive messages, the value should be set to
+ .Dq no .
++.It Cm TrustedUserCAKeys
++Specifies a file containing public keys of certificate authorities that are
++trusted sign user certificates for authentication.
++Keys are listed one per line, empty lines and comments starting with
++.Ql #
++are allowed.
++If a certificate is presented for authentication and has its signing CA key
++listed in this file, then it may be used for authentication for any user
++listed in the certificate's principals list.
++Note that certificates that lack a list of principals will not be permitted
++for authentication using
++.Cm TrustedUserCAKeys .
++For more details in certificates, please see the
++.Sx CERTIFICATES
++section in
++.Xr ssh-keygen 1 .
+ .It Cm UseDNS
+ Specifies whether
+ .Xr sshd 8
+diff -up openssh-5.3p1/ssh-dss.c.certificates openssh-5.3p1/ssh-dss.c
+--- openssh-5.3p1/ssh-dss.c.certificates	2013-07-18 15:38:20.282187082 +0200
++++ openssh-5.3p1/ssh-dss.c	2013-07-18 15:38:20.447186363 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: ssh-dss.c,v 1.24 2006/11/06 21:25:28 markus Exp $ */
++/* $OpenBSD: ssh-dss.c,v 1.26 2010/04/16 01:47:26 djm Exp $ */
+ /*
+  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+  *
+@@ -57,7 +57,8 @@ ssh_dss_sign(const Key *key, u_char **si
+ 	u_int rlen, slen, len, dlen;
+ 	Buffer b;
+ 
+-	if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) {
++	if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA &&
++	    key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) {
+ 		error("ssh_dss_sign: no DSA key");
+ 		return -1;
+ 	}
+@@ -150,7 +151,8 @@ ssh_dss_verify(const Key *key, const u_c
+ 	int rlen, ret;
+ 	Buffer b;
+ 
+-	if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) {
++	if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA &&
++	    key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) {
+ 		error("ssh_dss_verify: no DSA key");
+ 		return -1;
+ 	}
+diff -up openssh-5.3p1/ssh-keygen.1.certificates openssh-5.3p1/ssh-keygen.1
+--- openssh-5.3p1/ssh-keygen.1.certificates	2013-07-18 15:38:20.412186516 +0200
++++ openssh-5.3p1/ssh-keygen.1	2013-07-18 15:48:49.632444143 +0200
+@@ -109,6 +109,19 @@
+ .Nm ssh-keygen
+ .Op Fl n
+ .Op Fl D Ar smartcard
++.Nm ssh-keygen
++.Fl s Ar ca_key
++.Fl I Ar certificate_identity
++.Op Fl h
++.Op Fl n Ar principals
++.Op Fl O Ar option
++.Op Fl V Ar validity_interval
++.Op Fl z Ar serial_number
++.Ar
++.Nm ssh-keygen
++.Bk -words
++.Fl L
++.Op Fl f Ar input_keyfile
+ .Sh DESCRIPTION
+ .Nm
+ generates, manages and converts authentication keys for
+@@ -249,6 +262,17 @@ but they do not reveal identifying infor
+ be disclosed.
+ This option will not modify existing hashed hostnames and is therefore safe
+ to use on files that mix hashed and non-hashed names.
++.It Fl h
++When signing a key, create a host certificate instead of a user
++certificate.
++Please see the
++.Sx CERTIFICATES
++section for details.
++.It Fl I
++Specify the key identity when signing a public key.
++Please see the
++.Sx CERTIFICATES
++section for details.
+ .It Fl i
+ This option will read an unencrypted private (or public) key file
+ in SSH2-compatible format and print an OpenSSH compatible private
+@@ -258,6 +282,8 @@ also reads the
+ RFC 4716 SSH Public Key File Format.
+ This option allows importing keys from several commercial
+ SSH implementations.
++.It Fl L
++Prints the contents of a certificate.
+ .It Fl l
+ Show fingerprint of specified public key file.
+ Private RSA1 keys are also supported.
+@@ -274,6 +300,71 @@ candidate moduli for DH-GEX.
+ Extract the public key from smartcard.
+ .It Fl N Ar new_passphrase
+ Provides the new passphrase.
++.It Fl n Ar principals
++Specify one or more principals (user or host names) to be included in
++a certificate when signing a key.
++Multiple principals may be specified, separated by commas.
++Please see the
++.Sx CERTIFICATES
++section for details.
++.It Fl O Ar option
++Specify a certificate option when signing a key.
++This option may be specified multiple times.
++Please see the
++.Sx CERTIFICATES
++section for details.
++The options that are valid for user certificates are:
++.Bl -tag -width Ds
++.It Ic no-x11-forwarding
++Disable X11 forwarding. (permitted by default)
++.It Ic no-agent-forwarding
++Disable
++.Xr ssh-agent 1
++forwarding. (permitted by default)
++.It Ic no-port-forwarding
++Disable port forwarding. (permitted by default)
++.It Ic no-pty
++Disable PTY allocation. (permitted by default)
++.It Ic no-user-rc
++Disable execution of
++.Pa ~/.ssh/rc
++by
++.Xr sshd 8 .
++(permitted by default)
++.It Ic clear
++Clear all enabled permissions.
++This is useful for clearing the default set of permissions so permissions may
++be added individually.
++.It Ic permit-x11-forwarding
++Allows X11 forwarding.
++.It Ic permit-agent-forwarding
++Allows
++.Xr ssh-agent 1
++forwarding.
++.It Ic permit-port-forwarding
++Allows port forwarding.
++.It Ic permit-pty
++Allows PTY allocation.
++.It Ic permit-user-rc
++Allows execution of
++.Pa ~/.ssh/rc
++by
++.Xr sshd 8 .
++.It Ic force-command=command
++Forces the execution of
++.Ar command
++instead of any shell or command specified by the user when
++the certificate is used for authentication.
++.It Ic source-address=address_list
++Restrict the source addresses from which the certificate is considered valid
++from.
++The
++.Ar address_list
++is a comma-separated list of one or more address/netmask pairs in CIDR
++format.
++.El
++.Pp
++At present, no options are valid for host keys.
+ .It Fl P Ar passphrase
+ Provides the (old) passphrase.
+ .It Fl p
+@@ -303,6 +394,11 @@ Print the SSHFP fingerprint resource rec
+ for the specified public key file.
+ .It Fl S Ar start
+ Specify start point (in hex) when generating candidate moduli for DH-GEX.
++.It Fl s Ar ca_key
++Certify (sign) a public key using the specified CA key.
++Please see the
++.Sx CERTIFICATES
++section for details.
+ .It Fl T Ar output_file
+ Test DH group exchange candidate primes (generated using the
+ .Fl G
+@@ -316,6 +412,29 @@ for protocol version 1 and
+ or
+ .Dq dsa
+ for protocol version 2.
++.It Fl V Ar validity_interval
++Specify a validity interval when signing a certificate.
++A validity interval may consist of a single time, indicating that the
++certificate is valid beginning now and expiring at that time, or may consist
++of two times separated by a colon to indicate an explicit time interval.
++The start time may be specified as a date in YYYYMMDD format, a time
++in YYYYMMDDHHMMSS format or a relative time (to the current time) consisting
++of a minus sign followed by a relative time in the format described in the
++.Sx TIME FORMATS
++section of
++.Xr ssh_config 5 .
++The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMMSS time or
++a relative time starting with a plus character.
++.Pp
++For example:
++.Dq +52w1d
++(valid from now to 52 weeks and one day from now),
++.Dq -4w:+4w
++(valid from four weeks ago to four weeks from now),
++.Dq 20100101123000:20110101123000
++(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011),
++.Dq -1d:20110101
++(valid from yesterday to midnight, January 1st, 2011).
+ .It Fl v
+ Verbose mode.
+ Causes
+@@ -331,6 +450,10 @@ Specify desired generator when testing c
+ .It Fl y
+ This option will read a private
+ OpenSSH format file and print an OpenSSH public key to stdout.
++.It Fl z Ar serial_number
++Specifies a serial number to be embedded in the certificate to distinguish
++this certificate from others from the same CA.
++The default serial number is zero.
+ .El
+ .Sh MODULI GENERATION
+ .Nm
+@@ -386,6 +509,72 @@ Screened DH groups may be installed in
+ .Pa /etc/moduli .
+ It is important that this file contains moduli of a range of bit lengths and
+ that both ends of a connection share common moduli.
++.Sh CERTIFICATES
++.Nm
++supports signing of keys to produce certificates that may be used for
++user or host authentication.
++Certificates consist of a public key, some identity information, zero or
++more principal (user or host) names and a set of options that
++are signed by a Certification Authority (CA) key.
++Clients or servers may then trust only the CA key and verify its signature
++on a certificate rather than trusting many user/host keys.
++Note that OpenSSH certificates are a different, and much simpler, format to
++the X.509 certificates used in
++.Xr ssl 8 .
++.Pp
++.Nm
++supports two types of certificates: user and host.
++User certificates authenticate users to servers, whereas host certificates
++authenticate server hosts to users. To generate a user certificate:
++.Pp
++.Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub
++.Pp
++The resultant certificate will be placed in
++.Pa /path/to/user_key-cert.pub .
++A host certificate requires the
++.Fl h
++option:
++.Pp
++.Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub
++.Pp
++The host certificate will be output to
++.Pa /path/to/host_key-cert.pub .
++In both cases,
++.Ar key_id
++is a "key identifier" that is logged by the server when the certificate
++is used for authentication.
++.Pp
++Certificates may be limited to be valid for a set of principal (user/host)
++names.
++By default, generated certificates are valid for all users or hosts.
++To generate a certificate for a specified set of principals:
++.Pp
++.Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub
++.Dl "$ ssh-keygen -s ca_key -I key_id -h -n host.domain user_key.pub"
++.Pp
++Additional limitations on the validity and use of user certificates may
++be specified through certificate options.
++A certificate option may disable features of the SSH session, may be
++valid only when presented from particular source addresses or may
++force the use of a specific command.
++For a list of valid certificate options, see the documentation for the
++.Fl O
++option above.
++.Pp
++Finally, certificates may be defined with a validity lifetime.
++The
++.Fl V
++option allows specification of certificate start and end times.
++A certificate that is presented at a time outside this range will not be
++considered valid.
++By default, certificates have a maximum validity interval.
++.Pp
++For certificates to be used for user or host authentication, the CA
++public key must be trusted by
++.Xr sshd 8
++or
++.Xr ssh 1 .
++Please refer to those manual pages for details.
+ .Sh FILES
+ .Bl -tag -width Ds
+ .It Pa ~/.ssh/identity
+diff -up openssh-5.3p1/ssh-keygen.1.orig.certificates openssh-5.3p1/ssh-keygen.1.orig
+--- openssh-5.3p1/ssh-keygen.1.orig.certificates	2013-07-18 15:38:20.448186359 +0200
++++ openssh-5.3p1/ssh-keygen.1.orig	2013-07-18 15:38:20.448186359 +0200
+@@ -0,0 +1,672 @@
++.\"	$OpenBSD: ssh-keygen.1,v 1.79 2008/07/24 23:55:30 sthen Exp $
++.\"
++.\"  -*- nroff -*-
++.\"
++.\" Author: Tatu Ylonen <ylo at cs.hut.fi>
++.\" Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
++.\"                    All rights reserved
++.\"
++.\" As far as I am concerned, the code I have written for this software
++.\" can be used freely for any purpose.  Any derived versions of this
++.\" software must be clearly marked as such, and if the derived work is
++.\" incompatible with the protocol description in the RFC file, it must be
++.\" called by a name other than "ssh" or "Secure Shell".
++.\"
++.\"
++.\" Copyright (c) 1999,2000 Markus Friedl.  All rights reserved.
++.\" Copyright (c) 1999 Aaron Campbell.  All rights reserved.
++.\" Copyright (c) 1999 Theo de Raadt.  All rights reserved.
++.\"
++.\" Redistribution and use in source and binary forms, with or without
++.\" modification, are permitted provided that the following conditions
++.\" are met:
++.\" 1. Redistributions of source code must retain the above copyright
++.\"    notice, this list of conditions and the following disclaimer.
++.\" 2. Redistributions in binary form must reproduce the above copyright
++.\"    notice, this list of conditions and the following disclaimer in the
++.\"    documentation and/or other materials provided with the distribution.
++.\"
++.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
++.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
++.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
++.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++.\"
++.Dd $Mdocdate: July 24 2008 $
++.Dt SSH-KEYGEN 1
++.Os
++.Sh NAME
++.Nm ssh-keygen
++.Nd authentication key generation, management and conversion
++.Sh SYNOPSIS
++.Nm ssh-keygen
++.Bk -words
++.Op Fl q
++.Op Fl b Ar bits
++.Fl t Ar type
++.Op Fl N Ar new_passphrase
++.Op Fl C Ar comment
++.Op Fl f Ar output_keyfile
++.Ek
++.Nm ssh-keygen
++.Fl p
++.Op Fl P Ar old_passphrase
++.Op Fl N Ar new_passphrase
++.Op Fl f Ar keyfile
++.Nm ssh-keygen
++.Fl i
++.Op Fl f Ar input_keyfile
++.Nm ssh-keygen
++.Fl e
++.Op Fl f Ar input_keyfile
++.Nm ssh-keygen
++.Fl y
++.Op Fl f Ar input_keyfile
++.Nm ssh-keygen
++.Fl c
++.Op Fl P Ar passphrase
++.Op Fl C Ar comment
++.Op Fl f Ar keyfile
++.Nm ssh-keygen
++.Fl l
++.Op Fl f Ar input_keyfile
++.Nm ssh-keygen
++.Fl B
++.Op Fl f Ar input_keyfile
++.Nm ssh-keygen
++.Fl D Ar reader
++.Nm ssh-keygen
++.Fl F Ar hostname
++.Op Fl f Ar known_hosts_file
++.Op Fl l
++.Nm ssh-keygen
++.Fl H
++.Op Fl f Ar known_hosts_file
++.Nm ssh-keygen
++.Fl R Ar hostname
++.Op Fl f Ar known_hosts_file
++.Nm ssh-keygen
++.Fl U Ar reader
++.Op Fl f Ar input_keyfile
++.Nm ssh-keygen
++.Fl r Ar hostname
++.Op Fl f Ar input_keyfile
++.Op Fl g
++.Nm ssh-keygen
++.Fl G Ar output_file
++.Op Fl v
++.Op Fl b Ar bits
++.Op Fl M Ar memory
++.Op Fl S Ar start_point
++.Nm ssh-keygen
++.Fl T Ar output_file
++.Fl f Ar input_file
++.Op Fl v
++.Op Fl a Ar num_trials
++.Op Fl W Ar generator
++.Nm ssh-keygen
++.Op Fl n
++.Op Fl D Ar smartcard
++.Nm ssh-keygen
++.Fl s Ar ca_key
++.Fl I Ar certificate_identity
++.Op Fl h
++.Op Fl n Ar principals
++.Op Fl O Ar constraint
++.Op Fl V Ar validity_interval
++.Ar
++.Nm ssh-keygen
++.Bk -words
++.Fl L
++.Op Fl f Ar input_keyfile
++.Sh DESCRIPTION
++.Nm
++generates, manages and converts authentication keys for
++.Xr ssh 1 .
++.Nm
++can create RSA keys for use by SSH protocol version 1 and RSA or DSA
++keys for use by SSH protocol version 2.
++The type of key to be generated is specified with the
++.Fl t
++option.
++If invoked without any arguments,
++.Nm
++will generate an RSA key for use in SSH protocol 2 connections.
++.Pp
++.Nm
++is also used to generate groups for use in Diffie-Hellman group
++exchange (DH-GEX).
++See the
++.Sx MODULI GENERATION
++section for details.
++.Pp
++Normally each user wishing to use SSH
++with RSA or DSA authentication runs this once to create the authentication
++key in
++.Pa ~/.ssh/identity ,
++.Pa ~/.ssh/id_dsa
++or
++.Pa ~/.ssh/id_rsa .
++Additionally, the system administrator may use this to generate host keys,
++as seen in
++.Pa /etc/rc .
++.Pp
++Normally this program generates the key and asks for a file in which
++to store the private key.
++The public key is stored in a file with the same name but
++.Dq .pub
++appended.
++The program also asks for a passphrase.
++The passphrase may be empty to indicate no passphrase
++(host keys must have an empty passphrase), or it may be a string of
++arbitrary length.
++A passphrase is similar to a password, except it can be a phrase with a
++series of words, punctuation, numbers, whitespace, or any string of
++characters you want.
++Good passphrases are 10-30 characters long, are
++not simple sentences or otherwise easily guessable (English
++prose has only 1-2 bits of entropy per character, and provides very bad
++passphrases), and contain a mix of upper and lowercase letters,
++numbers, and non-alphanumeric characters.
++The passphrase can be changed later by using the
++.Fl p
++option.
++.Pp
++There is no way to recover a lost passphrase.
++If the passphrase is
++lost or forgotten, a new key must be generated and copied to the
++corresponding public key to other machines.
++.Pp
++For RSA1 keys,
++there is also a comment field in the key file that is only for
++convenience to the user to help identify the key.
++The comment can tell what the key is for, or whatever is useful.
++The comment is initialized to
++.Dq user at host
++when the key is created, but can be changed using the
++.Fl c
++option.
++.Pp
++After a key is generated, instructions below detail where the keys
++should be placed to be activated.
++.Pp
++The options are as follows:
++.Bl -tag -width Ds
++.It Fl a Ar trials
++Specifies the number of primality tests to perform when screening DH-GEX
++candidates using the
++.Fl T
++command.
++.It Fl B
++Show the bubblebabble digest of specified private or public key file.
++.It Fl b Ar bits
++Specifies the number of bits in the key to create.
++For RSA keys, the minimum size is 768 bits and the default is 2048 bits.
++Generally, 2048 bits is considered sufficient.
++DSA keys must be exactly 1024 bits as specified by FIPS 186-2.
++.It Fl C Ar comment
++Provides a new comment.
++.It Fl c
++Requests changing the comment in the private and public key files.
++This operation is only supported for RSA1 keys.
++The program will prompt for the file containing the private keys, for
++the passphrase if the key has one, and for the new comment.
++.It Fl D Ar reader
++Download the RSA public key stored in the smartcard in
++.Ar reader .
++.It Fl e
++This option will read a private or public OpenSSH key file and
++print the key in
++RFC 4716 SSH Public Key File Format
++to stdout.
++This option allows exporting keys for use by several commercial
++SSH implementations.
++.It Fl F Ar hostname
++Search for the specified
++.Ar hostname
++in a
++.Pa known_hosts
++file, listing any occurrences found.
++This option is useful to find hashed host names or addresses and may also be
++used in conjunction with the
++.Fl H
++option to print found keys in a hashed format.
++.It Fl f Ar filename
++Specifies the filename of the key file.
++.It Fl G Ar output_file
++Generate candidate primes for DH-GEX.
++These primes must be screened for
++safety (using the
++.Fl T
++option) before use.
++.It Fl g
++Use generic DNS format when printing fingerprint resource records using the
++.Fl r
++command.
++.It Fl H
++Hash a
++.Pa known_hosts
++file.
++This replaces all hostnames and addresses with hashed representations
++within the specified file; the original content is moved to a file with
++a .old suffix.
++These hashes may be used normally by
++.Nm ssh
++and
++.Nm sshd ,
++but they do not reveal identifying information should the file's contents
++be disclosed.
++This option will not modify existing hashed hostnames and is therefore safe
++to use on files that mix hashed and non-hashed names.
++.It Fl h
++When signing a key, create a host certificate instead of a user
++certificate.
++Please see the
++.Sx CERTIFICATES
++section for details.
++.It Fl I
++Specify the key identity when signing a public key.
++Please see the
++.Sx CERTIFICATES
++section for details.
++.It Fl i
++This option will read an unencrypted private (or public) key file
++in SSH2-compatible format and print an OpenSSH compatible private
++(or public) key to stdout.
++.Nm
++also reads the
++RFC 4716 SSH Public Key File Format.
++This option allows importing keys from several commercial
++SSH implementations.
++.It Fl L
++Prints the contents of a certificate.
++.It Fl l
++Show fingerprint of specified public key file.
++Private RSA1 keys are also supported.
++For RSA and DSA keys
++.Nm
++tries to find the matching public key file and prints its fingerprint.
++If combined with
++.Fl v ,
++an ASCII art representation of the key is supplied with the fingerprint.
++.It Fl M Ar memory
++Specify the amount of memory to use (in megabytes) when generating
++candidate moduli for DH-GEX.
++.It Fl n
++Extract the public key from smartcard.
++.It Fl N Ar new_passphrase
++Provides the new passphrase.
++.It Fl n Ar principals
++Specify one or more principals (user or host names) to be included in
++a certificate when signing a key.
++Multiple principals may be specified, separated by commas.
++Please see the
++.Sx CERTIFICATES
++section for details.
++.It Fl O Ar constraint
++Specify a certificate constraint when signing a key.
++This option may be specified multiple times.
++Please see the
++.Sx CERTIFICATES
++section for details.
++The constraints that are valid for user certificates are:
++.Bl -tag -width Ds
++.It Ic no-x11-forwarding
++Disable X11 forwarding. (permitted by default)
++.It Ic no-agent-forwarding
++Disable
++.Xr ssh-agent 1
++forwarding. (permitted by default)
++.It Ic no-port-forwarding
++Disable port forwarding. (permitted by default)
++.It Ic no-pty
++Disable PTY allocation. (permitted by default)
++.It Ic no-user-rc
++Disable execution of
++.Pa ~/.ssh/rc
++by
++.Xr sshd 8 .
++(permitted by default)
++.It Ic clear
++Clear all enabled permissions.
++This is useful for clearing the default set of permissions so permissions may
++be added individually.
++.It Ic permit-x11-forwarding
++Allows X11 forwarding.
++.It Ic permit-port-forwarding
++Allows port forwarding.
++.It Ic permit-pty
++Allows PTY allocation.
++.It Ic permit-user-rc
++Allows execution of
++.Pa ~/.ssh/rc
++by
++.Xr sshd 8 .
++.It Ic force-command=command
++Forces the execution of
++.Ar command
++instead of any shell or command specified by the user when
++the certificate is used for authentication.
++.It Ic source-address=address_list
++Restrict the source addresses from which the certificate is considered valid
++from.
++The
++.Ar address_list
++is a comma-separated list of one or more address/netmask pairs in CIDR
++format.
++.El
++.Pp
++At present, no constraints are valid for host keys.
++.It Fl P Ar passphrase
++Provides the (old) passphrase.
++.It Fl p
++Requests changing the passphrase of a private key file instead of
++creating a new private key.
++The program will prompt for the file
++containing the private key, for the old passphrase, and twice for the
++new passphrase.
++.It Fl q
++Silence
++.Nm ssh-keygen .
++Used by
++.Pa /etc/rc
++when creating a new key.
++.It Fl R Ar hostname
++Removes all keys belonging to
++.Ar hostname
++from a
++.Pa known_hosts
++file.
++This option is useful to delete hashed hosts (see the
++.Fl H
++option above).
++.It Fl r Ar hostname
++Print the SSHFP fingerprint resource record named
++.Ar hostname
++for the specified public key file.
++.It Fl S Ar start
++Specify start point (in hex) when generating candidate moduli for DH-GEX.
++.It Fl s Ar ca_key
++Certify (sign) a public key using the specified CA key.
++Please see the
++.Sx CERTIFICATES
++section for details.
++.It Fl T Ar output_file
++Test DH group exchange candidate primes (generated using the
++.Fl G
++option) for safety.
++.It Fl t Ar type
++Specifies the type of key to create.
++The possible values are
++.Dq rsa1
++for protocol version 1 and
++.Dq rsa
++or
++.Dq dsa
++for protocol version 2.
++.It Fl U Ar reader
++Upload an existing RSA private key into the smartcard in
++.Ar reader .
++.It Fl V Ar validity_interval
++Specify a validity interval when signing a certificate.
++A validity interval may consist of a single time, indicating that the
++certificate is valid beginning now and expiring at that time, or may consist
++of two times separated by a colon to indicate an explicit time interval.
++The start time may be specified as a date in YYYYMMDD format, a time
++in YYYYMMDDHHMMSS format or a relative time (to the current time) consisting
++of a minus sign followed by a relative time in the format described in the
++.Sx TIME FORMATS
++section of
++.Xr ssh_config 5 .
++The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMMSS time or
++a relative time starting with a plus character.
++.Pp
++For example:
++.Dq +52w1d
++(valid from now to 52 weeks and one day from now),
++.Dq -4w:+4w
++(valid from four weeks ago to four weeks from now),
++.Dq 20100101123000:20110101123000
++(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011),
++.Dq -1d:20110101
++(valid from yesterday to midnight, January 1st, 2011).
++.It Fl v
++Verbose mode.
++Causes
++.Nm
++to print debugging messages about its progress.
++This is helpful for debugging moduli generation.
++Multiple
++.Fl v
++options increase the verbosity.
++The maximum is 3.
++.It Fl W Ar generator
++Specify desired generator when testing candidate moduli for DH-GEX.
++.It Fl y
++This option will read a private
++OpenSSH format file and print an OpenSSH public key to stdout.
++.El
++.Sh MODULI GENERATION
++.Nm
++may be used to generate groups for the Diffie-Hellman Group Exchange
++(DH-GEX) protocol.
++Generating these groups is a two-step process: first, candidate
++primes are generated using a fast, but memory intensive process.
++These candidate primes are then tested for suitability (a CPU-intensive
++process).
++.Pp
++Generation of primes is performed using the
++.Fl G
++option.
++The desired length of the primes may be specified by the
++.Fl b
++option.
++For example:
++.Pp
++.Dl # ssh-keygen -G moduli-2048.candidates -b 2048
++.Pp
++By default, the search for primes begins at a random point in the
++desired length range.
++This may be overridden using the
++.Fl S
++option, which specifies a different start point (in hex).
++.Pp
++Once a set of candidates have been generated, they must be tested for
++suitability.
++This may be performed using the
++.Fl T
++option.
++In this mode
++.Nm
++will read candidates from standard input (or a file specified using the
++.Fl f
++option).
++For example:
++.Pp
++.Dl # ssh-keygen -T moduli-2048 -f moduli-2048.candidates
++.Pp
++By default, each candidate will be subjected to 100 primality tests.
++This may be overridden using the
++.Fl a
++option.
++The DH generator value will be chosen automatically for the
++prime under consideration.
++If a specific generator is desired, it may be requested using the
++.Fl W
++option.
++Valid generator values are 2, 3, and 5.
++.Pp
++Screened DH groups may be installed in
++.Pa /etc/moduli .
++It is important that this file contains moduli of a range of bit lengths and
++that both ends of a connection share common moduli.
++.Sh CERTIFICATES
++.Nm
++supports signing of keys to produce certificates that may be used for
++user or host authentication.
++Certificates consist of a public key, some identity information, zero or
++more principal (user or host) names and an optional set of constraints that
++are signed by a Certification Authority (CA) key.
++Clients or servers may then trust only the CA key and verify its signature
++on a certificate rather than trusting many user/host keys.
++Note that OpenSSH certificates are a different, and much simpler, format to
++the X.509 certificates used in
++.Xr ssl 8 .
++.Pp
++.Nm
++supports two types of certificates: user and host.
++User certificates authenticate users to servers, whereas host certificates
++authenticate server hosts to users. To generate a user certificate:
++.Pp
++.Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub
++.Pp
++The resultant certificate will be placed in
++.Pa /path/to/user_key_cert.pub .
++A host certificate requires the
++.Fl h
++option:
++.Pp
++.Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub
++.Pp
++The host certificate will be output to
++.Pa /path/to/host_key_cert.pub .
++In both cases,
++.Ar key_id
++is a "key identifier" that is logged by the server when the certificate
++is used for authentication.
++.Pp
++Certificates may be limited to be valid for a set of principal (user/host)
++names.
++By default, generated certificates are valid for all users or hosts.
++To generate a certificate for a specified set of principals:
++.Pp
++.Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub
++.Dl $ ssh-keygen -s ca_key -I key_id -h -n host.domain user_key.pub
++.Pp
++Additional limitations on the validity and use of user certificates may
++be specified through certificate constraints.
++A constrained certificate may disable features of the SSH session, may be
++valid only when presented from particular source addresses or may
++force the use of a specific command.
++For a list of valid certificate constraints, see the documentation for the
++.Fl O
++option above.
++.Pp
++Finally, certificates may be defined with a validity lifetime.
++The
++.Fl V
++option allows specification of certificate start and end times.
++A certificate that is presented at a time outside this range will not be
++considered valid.
++By default, certificates have a maximum validity interval.
++.Pp
++For certificates to be used for user or host authentication, the CA
++public key must be trusted by
++.Xr sshd 8
++or
++.Xr ssh 1 .
++Please refer to those manual pages for details.
++.Sh FILES
++.Bl -tag -width Ds
++.It Pa ~/.ssh/identity
++Contains the protocol version 1 RSA authentication identity of the user.
++This file should not be readable by anyone but the user.
++It is possible to
++specify a passphrase when generating the key; that passphrase will be
++used to encrypt the private part of this file using 3DES.
++This file is not automatically accessed by
++.Nm
++but it is offered as the default file for the private key.
++.Xr ssh 1
++will read this file when a login attempt is made.
++.It Pa ~/.ssh/identity.pub
++Contains the protocol version 1 RSA public key for authentication.
++The contents of this file should be added to
++.Pa ~/.ssh/authorized_keys
++on all machines
++where the user wishes to log in using RSA authentication.
++There is no need to keep the contents of this file secret.
++.It Pa ~/.ssh/id_dsa
++Contains the protocol version 2 DSA authentication identity of the user.
++This file should not be readable by anyone but the user.
++It is possible to
++specify a passphrase when generating the key; that passphrase will be
++used to encrypt the private part of this file using 3DES.
++This file is not automatically accessed by
++.Nm
++but it is offered as the default file for the private key.
++.Xr ssh 1
++will read this file when a login attempt is made.
++.It Pa ~/.ssh/id_dsa.pub
++Contains the protocol version 2 DSA public key for authentication.
++The contents of this file should be added to
++.Pa ~/.ssh/authorized_keys
++on all machines
++where the user wishes to log in using public key authentication.
++There is no need to keep the contents of this file secret.
++.It Pa ~/.ssh/id_rsa
++Contains the protocol version 2 RSA authentication identity of the user.
++This file should not be readable by anyone but the user.
++It is possible to
++specify a passphrase when generating the key; that passphrase will be
++used to encrypt the private part of this file using 3DES.
++This file is not automatically accessed by
++.Nm
++but it is offered as the default file for the private key.
++.Xr ssh 1
++will read this file when a login attempt is made.
++.It Pa ~/.ssh/id_rsa.pub
++Contains the protocol version 2 RSA public key for authentication.
++The contents of this file should be added to
++.Pa ~/.ssh/authorized_keys
++on all machines
++where the user wishes to log in using public key authentication.
++There is no need to keep the contents of this file secret.
++.It Pa /etc/moduli
++Contains Diffie-Hellman groups used for DH-GEX.
++The file format is described in
++.Xr moduli 5 .
++.El
++.Sh ENVIRONMENT
++.Bl -tag -width Ds -compact
++.Pp
++.It Pa SSH_USE_STRONG_RNG
++The reseeding of the OpenSSL random generator is usually done from
++.Cm /dev/urandom .
++If the 
++.Cm SSH_USE_STRONG_RNG
++environment variable is set to value other than
++.Cm 0
++the OpenSSL random generator is reseeded from
++.Cm /dev/random .
++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. 
++Minimum is 6 bytes.
++This setting is not recommended on the computers without the hardware
++random generator because insufficient entropy causes the connection to 
++be blocked until enough entropy is available.
++.El
++.Sh SEE ALSO
++.Xr ssh 1 ,
++.Xr ssh-add 1 ,
++.Xr ssh-agent 1 ,
++.Xr moduli 5 ,
++.Xr sshd 8
++.Rs
++.%R RFC 4716
++.%T "The Secure Shell (SSH) Public Key File Format"
++.%D 2006
++.Re
++.Sh AUTHORS
++OpenSSH is a derivative of the original and free
++ssh 1.2.12 release by Tatu Ylonen.
++Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos,
++Theo de Raadt and Dug Song
++removed many bugs, re-added newer features and
++created OpenSSH.
++Markus Friedl contributed the support for SSH
++protocol versions 1.5 and 2.0.
+diff -up openssh-5.3p1/ssh-keygen.c.certificates openssh-5.3p1/ssh-keygen.c
+--- openssh-5.3p1/ssh-keygen.c.certificates	2013-07-18 15:38:20.413186512 +0200
++++ openssh-5.3p1/ssh-keygen.c	2013-07-18 15:46:08.488146441 +0200
+@@ -49,6 +49,7 @@
+ #include "match.h"
+ #include "hostfile.h"
+ #include "dns.h"
++#include "ssh2.h"
+ 
+ #ifdef ENABLE_PKCS11
+ #include "ssh-pkcs11.h"
+@@ -87,6 +88,9 @@ int find_host = 0;
+ /* Flag indicating that we want to delete a host from a known_hosts file */
+ int delete_host = 0;
+ 
++/* Flag indicating that we want to show the contents of a certificate */
++int show_cert = 0;
++
+ /* Flag indicating that we just want to see the key fingerprint */
+ int print_fingerprint = 0;
+ int print_bubblebabble = 0;
+@@ -104,6 +108,37 @@ char *identity_new_passphrase = NULL;
+ /* This is set to the new comment if given on the command line. */
+ char *identity_comment = NULL;
+ 
++/* Path to CA key when certifying keys. */
++char *ca_key_path = NULL;
++
++/* Certificate serial number */
++long long cert_serial = 0;
++
++/* Key type when certifying */
++u_int cert_key_type = SSH2_CERT_TYPE_USER;
++
++/* "key ID" of signed key */
++char *cert_key_id = NULL;
++
++/* Comma-separated list of principal names for certifying keys */
++char *cert_principals = NULL;
++
++/* Validity period for certificates */
++u_int64_t cert_valid_from = 0;
++u_int64_t cert_valid_to = ~0ULL;
++
++/* Certificate options */
++#define CERTOPT_X_FWD	(1)
++#define CERTOPT_AGENT_FWD	(1<<1)
++#define CERTOPT_PORT_FWD	(1<<2)
++#define CERTOPT_PTY		(1<<3)
++#define CERTOPT_USER_RC	(1<<4)
++#define CERTOPT_DEFAULT	(CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \
++			 CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC)
++u_int32_t certflags_flags = CERTOPT_DEFAULT;
++char *certflags_command = NULL;
++char *certflags_src_addr = NULL;
++
+ /* Dump public key file in format used by real and the original SSH 2 */
+ int convert_to_ssh2 = 0;
+ int convert_from_ssh2 = 0;
+@@ -134,9 +169,13 @@ ask_filename(struct passwd *pw, const ch
+ 		case KEY_RSA1:
+ 			name = _PATH_SSH_CLIENT_IDENTITY;
+ 			break;
++		case KEY_DSA_CERT:
++		case KEY_DSA_CERT_V00:
+ 		case KEY_DSA:
+ 			name = _PATH_SSH_CLIENT_ID_DSA;
+ 			break;
++		case KEY_RSA_CERT:
++		case KEY_RSA_CERT_V00:
+ 		case KEY_RSA:
+ 			name = _PATH_SSH_CLIENT_ID_RSA;
+ 			break;
+@@ -613,7 +652,7 @@ do_fingerprint(struct passwd *pw)
+ }
+ 
+ static void
+-print_host(FILE *f, const char *name, Key *public, int hash)
++printhost(FILE *f, const char *name, Key *public, int ca, int hash)
+ {
+ 	if (print_fingerprint) {
+ 		enum fp_rep rep;
+@@ -633,7 +672,7 @@ print_host(FILE *f, const char *name, Ke
+ 	} else {
+ 		if (hash && (name = host_hash(name, NULL, 0)) == NULL)
+ 			fatal("hash_host failed");
+-		fprintf(f, "%s ", name);
++		fprintf(f, "%s%s%s ", ca ? CA_MARKER : "", ca ? " " : "", name);
+ 		if (!key_write(public, f))
+ 			fatal("key_write failed");
+ 		fprintf(f, "\n");
+@@ -644,10 +683,11 @@ static void
+ do_known_hosts(struct passwd *pw, const char *name)
+ {
+ 	FILE *in, *out = stdout;
+-	Key *public;
++	Key *pub;
+ 	char *cp, *cp2, *kp, *kp2;
+ 	char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN];
+ 	int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0;
++	int ca;
+ 
+ 	if (!have_identity) {
+ 		cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
+@@ -703,9 +743,19 @@ do_known_hosts(struct passwd *pw, const
+ 				fprintf(out, "%s\n", cp);
+ 			continue;
+ 		}
++		/* Check whether this is a CA key */
++		if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 &&
++		    (cp[sizeof(CA_MARKER) - 1] == ' ' ||
++		    cp[sizeof(CA_MARKER) - 1] == '\t')) {
++			ca = 1;
++			cp += sizeof(CA_MARKER);
++		} else
++			ca = 0;
++
+ 		/* Find the end of the host name portion. */
+ 		for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++)
+ 			;
++
+ 		if (*kp == '\0' || *(kp + 1) == '\0') {
+ 			error("line %d missing key: %.40s...",
+ 			    num, line);
+@@ -715,15 +765,15 @@ do_known_hosts(struct passwd *pw, const
+ 		*kp++ = '\0';
+ 		kp2 = kp;
+ 
+-		public = key_new(KEY_RSA1);
+-		if (key_read(public, &kp) != 1) {
++		pub = key_new(KEY_RSA1);
++		if (key_read(pub, &kp) != 1) {
+ 			kp = kp2;
+-			key_free(public);
+-			public = key_new(KEY_UNSPEC);
+-			if (key_read(public, &kp) != 1) {
++			key_free(pub);
++			pub = key_new(KEY_UNSPEC);
++			if (key_read(pub, &kp) != 1) {
+ 				error("line %d invalid key: %.40s...",
+ 				    num, line);
+-				key_free(public);
++				key_free(pub);
+ 				invalid = 1;
+ 				continue;
+ 			}
+@@ -741,43 +791,52 @@ do_known_hosts(struct passwd *pw, const
+ 				c = (strcmp(cp2, cp) == 0);
+ 				if (find_host && c) {
+ 					printf("# Host %s found: "
+-					    "line %d type %s\n", name,
+-					    num, key_type(public));
+-					print_host(out, cp, public, 0);
++					    "line %d type %s%s\n", name,
++					    num, key_type(pub),
++					    ca ? " (CA key)" : "");
++					printhost(out, cp, pub, ca, 0);
+ 				}
+-				if (delete_host && !c)
+-					print_host(out, cp, public, 0);
++				if (delete_host && !c && !ca)
++					printhost(out, cp, pub, ca, 0);
+ 			} else if (hash_hosts)
+-				print_host(out, cp, public, 0);
++				printhost(out, cp, pub, ca, 0);
+ 		} else {
+ 			if (find_host || delete_host) {
+ 				c = (match_hostname(name, cp,
+ 				    strlen(cp)) == 1);
+ 				if (find_host && c) {
+ 					printf("# Host %s found: "
+-					    "line %d type %s\n", name,
+-					    num, key_type(public));
+-					print_host(out, name, public,
+-					    hash_hosts);
++					    "line %d type %s%s\n", name,
++					    num, key_type(pub),
++					    ca ? " (CA key)" : "");
++					printhost(out, name, pub,
++					    ca, hash_hosts && !ca);
+ 				}
+-				if (delete_host && !c)
+-					print_host(out, cp, public, 0);
++				if (delete_host && !c && !ca)
++					printhost(out, cp, pub, ca, 0);
+ 			} else if (hash_hosts) {
+ 				for (cp2 = strsep(&cp, ",");
+ 				    cp2 != NULL && *cp2 != '\0';
+ 				    cp2 = strsep(&cp, ",")) {
+-					if (strcspn(cp2, "*?!") != strlen(cp2))
++					if (ca) {
++						fprintf(stderr, "Warning: "
++						    "ignoring CA key for host: "
++						    "%.64s\n", cp2);
++						printhost(out, cp2, pub, ca, 0);
++					} else if (strcspn(cp2, "*?!") !=
++					    strlen(cp2)) {
+ 						fprintf(stderr, "Warning: "
+ 						    "ignoring host name with "
+ 						    "metacharacters: %.64s\n",
+ 						    cp2);
+-					else
+-						print_host(out, cp2, public, 1);
++						printhost(out, cp2, pub, ca, 0);
++					} else
++						printhost(out, cp2, pub, ca, 1);
+ 				}
+ 				has_unhashed = 1;
+ 			}
+ 		}
+-		key_free(public);
++		key_free(pub);
+ 	}
+ 	fclose(in);
+ 
+@@ -1034,6 +1093,439 @@ do_change_comment(struct passwd *pw)
+ 	exit(0);
+ }
+ 
++static const char *
++fmt_validity(u_int64_t valid_from, u_int64_t valid_to)
++{
++	char from[32], to[32];
++	static char ret[64];
++	time_t tt;
++	struct tm *tm;
++
++	*from = *to = '\0';
++	if (valid_from == 0 && valid_to == 0xffffffffffffffffULL)
++		return "forever";
++
++	if (valid_from != 0) {
++		/* XXX revisit INT_MAX in 2038 :) */
++		tt = valid_from > INT_MAX ? INT_MAX : valid_from;
++		tm = localtime(&tt);
++		strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm);
++	}
++	if (valid_to != 0xffffffffffffffffULL) {
++		/* XXX revisit INT_MAX in 2038 :) */
++		tt = valid_to > INT_MAX ? INT_MAX : valid_to;
++		tm = localtime(&tt);
++		strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm);
++	}
++
++	if (valid_from == 0) {
++		snprintf(ret, sizeof(ret), "before %s", to);
++		return ret;
++	}
++	if (valid_to == 0xffffffffffffffffULL) {
++		snprintf(ret, sizeof(ret), "after %s", from);
++		return ret;
++	}
++
++	snprintf(ret, sizeof(ret), "from %s to %s", from, to);
++	return ret;
++}
++
++static void
++add_flag_option(Buffer *c, const char *name)
++{
++	debug3("%s: %s", __func__, name);
++	buffer_put_cstring(c, name);
++	buffer_put_string(c, NULL, 0);
++}
++
++static void
++add_string_option(Buffer *c, const char *name, const char *value)
++{
++	Buffer b;
++
++	debug3("%s: %s=%s", __func__, name, value);
++	buffer_init(&b);
++	buffer_put_cstring(&b, value);
++
++	buffer_put_cstring(c, name);
++	buffer_put_string(c, buffer_ptr(&b), buffer_len(&b));
++
++	buffer_free(&b);
++}
++
++#define OPTIONS_CRITICAL	1
++#define OPTIONS_EXTENSIONS	2
++static void
++prepare_options_buf(Buffer *c, int which)
++{
++	buffer_clear(c);
++	if ((which & OPTIONS_EXTENSIONS) != 0 &&
++	    (certflags_flags & CERTOPT_X_FWD) != 0)
++		add_flag_option(c, "permit-X11-forwarding");
++	if ((which & OPTIONS_EXTENSIONS) != 0 &&
++	    (certflags_flags & CERTOPT_AGENT_FWD) != 0)
++		add_flag_option(c, "permit-agent-forwarding");
++	if ((which & OPTIONS_EXTENSIONS) != 0 &&
++	    (certflags_flags & CERTOPT_PORT_FWD) != 0)
++		add_flag_option(c, "permit-port-forwarding");
++	if ((which & OPTIONS_EXTENSIONS) != 0 &&
++	    (certflags_flags & CERTOPT_PTY) != 0)
++		add_flag_option(c, "permit-pty");
++	if ((which & OPTIONS_EXTENSIONS) != 0 &&
++	    (certflags_flags & CERTOPT_USER_RC) != 0)
++		add_flag_option(c, "permit-user-rc");
++	if ((which & OPTIONS_CRITICAL) != 0 &&
++	    certflags_command != NULL)
++		add_string_option(c, "force-command", certflags_command);
++	if ((which & OPTIONS_CRITICAL) != 0 &&
++	    certflags_src_addr != NULL)
++		add_string_option(c, "source-address", certflags_src_addr);
++}
++
++static void
++do_ca_sign(struct passwd *pw, int argc, char **argv)
++{
++	int i, fd;
++	u_int n;
++	Key *ca, *public;
++	char *otmp, *tmp, *cp, *out, *comment, **plist = NULL;
++	FILE *f;
++	int v00 = 0; /* legacy keys */
++
++	tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
++	if ((ca = load_identity(tmp)) == NULL)
++		fatal("Couldn't load CA key \"%s\"", tmp);
++	xfree(tmp);
++
++	if (key_type_name != NULL) {
++		switch (key_type_from_name(key_type_name)) {
++		case KEY_RSA_CERT_V00:
++		case KEY_DSA_CERT_V00:
++			v00 = 1;
++			break;
++		case KEY_UNSPEC:
++			if (strcasecmp(key_type_name, "v00") == 0) {
++				v00 = 1;
++				break;
++			} else if (strcasecmp(key_type_name, "v01") == 0)
++				break;
++			/* FALLTHROUGH */
++		default:
++			fprintf(stderr, "unknown key type %s\n", key_type_name);
++			exit(1);
++		}
++	}
++
++	for (i = 0; i < argc; i++) {
++		/* Split list of principals */
++		n = 0;
++		if (cert_principals != NULL) {
++			otmp = tmp = xstrdup(cert_principals);
++			plist = NULL;
++			for (; (cp = strsep(&tmp, ",")) != NULL; n++) {
++				plist = xrealloc(plist, n + 1, sizeof(*plist));
++				if (*(plist[n] = xstrdup(cp)) == '\0')
++					fatal("Empty principal name");
++			}
++			xfree(otmp);
++		}
++	
++		tmp = tilde_expand_filename(argv[i], pw->pw_uid);
++		if ((public = key_load_public(tmp, &comment)) == NULL)
++			fatal("%s: unable to open \"%s\"", __func__, tmp);
++		if (public->type != KEY_RSA && public->type != KEY_DSA)
++			fatal("%s: key \"%s\" type %s cannot be certified",
++			    __func__, tmp, key_type(public));
++
++		/* Prepare certificate to sign */
++		if (key_to_certified(public, v00) != 0)
++			fatal("Could not upgrade key %s to certificate", tmp);
++		public->cert->type = cert_key_type;
++		public->cert->serial = (u_int64_t)cert_serial;
++		public->cert->key_id = xstrdup(cert_key_id);
++		public->cert->nprincipals = n;
++		public->cert->principals = plist;
++		public->cert->valid_after = cert_valid_from;
++		public->cert->valid_before = cert_valid_to;
++		if (v00) {
++			prepare_options_buf(&public->cert->critical,
++			    OPTIONS_CRITICAL|OPTIONS_EXTENSIONS);
++		} else {
++			prepare_options_buf(&public->cert->critical,
++			    OPTIONS_CRITICAL);
++			prepare_options_buf(&public->cert->extensions,
++			    OPTIONS_EXTENSIONS);
++		}
++		public->cert->signature_key = key_from_private(ca);
++
++		if (key_certify(public, ca) != 0)
++			fatal("Couldn't not certify key %s", tmp);
++
++		if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
++			*cp = '\0';
++		xasprintf(&out, "%s-cert.pub", tmp);
++		xfree(tmp);
++
++		if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
++			fatal("Could not open \"%s\" for writing: %s", out,
++			    strerror(errno));
++		if ((f = fdopen(fd, "w")) == NULL)
++			fatal("%s: fdopen: %s", __func__, strerror(errno));
++		if (!key_write(public, f))
++			fatal("Could not write certified key to %s", out);
++		fprintf(f, " %s\n", comment);
++		fclose(f);
++
++		if (!quiet) {
++			logit("Signed %s key %s: id \"%s\" serial %llu%s%s "
++			    "valid %s", key_cert_type(public), 
++			    out, public->cert->key_id, public->cert->serial,
++			    cert_principals != NULL ? " for " : "",
++			    cert_principals != NULL ? cert_principals : "",
++			    fmt_validity(cert_valid_from, cert_valid_to));
++		}
++
++		key_free(public);
++		xfree(out);
++	}
++	exit(0);
++}
++
++static u_int64_t
++parse_relative_time(const char *s, time_t now)
++{
++	int64_t mul, secs;
++
++	mul = *s == '-' ? -1 : 1;
++
++	if ((secs = convtime(s + 1)) == -1)
++		fatal("Invalid relative certificate time %s", s);
++	if (mul == -1 && secs > now)
++		fatal("Certificate time %s cannot be represented", s);
++	return now + (u_int64_t)(secs * mul);
++}
++
++static u_int64_t
++parse_absolute_time(const char *s)
++{
++	struct tm tm;
++	time_t tt;
++
++	if (strlen(s) != 8 && strlen(s) != 14)
++		fatal("Invalid certificate time format %s", s);
++
++	bzero(&tm, sizeof(tm));
++	if (strptime(s,
++	    strlen(s) == 8 ? "%Y%m%d" : "%Y%m%d%H%M%S", &tm) == NULL)
++		fatal("Invalid certificate time %s", s);
++	if ((tt = mktime(&tm)) < 0)
++		fatal("Certificate time %s cannot be represented", s);
++	return (u_int64_t)tt;
++}
++
++static void
++parse_cert_times(char *timespec)
++{
++	char *from, *to;
++	time_t now = time(NULL);
++	int64_t secs;
++
++	/* +timespec relative to now */
++	if (*timespec == '+' && strchr(timespec, ':') == NULL) {
++		if ((secs = convtime(timespec + 1)) == -1)
++			fatal("Invalid relative certificate life %s", timespec);
++		cert_valid_to = now + secs;
++		/*
++		 * Backdate certificate one minute to avoid problems on hosts
++		 * with poorly-synchronised clocks.
++		 */
++		cert_valid_from = ((now - 59)/ 60) * 60;
++		return;
++	}
++
++	/*
++	 * from:to, where
++	 * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
++	 *   to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
++	 */
++	from = xstrdup(timespec);
++	to = strchr(from, ':');
++	if (to == NULL || from == to || *(to + 1) == '\0')
++		fatal("Invalid certificate life specification %s", optarg);
++	*to++ = '\0';
++
++	if (*from == '-' || *from == '+')
++		cert_valid_from = parse_relative_time(from, now);
++	else
++		cert_valid_from = parse_absolute_time(from);
++
++	if (*to == '-' || *to == '+')
++		cert_valid_to = parse_relative_time(to, cert_valid_from);
++	else
++		cert_valid_to = parse_absolute_time(to);
++
++	if (cert_valid_to <= cert_valid_from)
++		fatal("Empty certificate validity interval");
++	xfree(from);
++}
++
++static void
++add_cert_option(char *opt)
++{
++	char *val;
++
++	if (strcmp(opt, "clear") == 0)
++		certflags_flags = 0;
++	else if (strcasecmp(opt, "no-x11-forwarding") == 0)
++		certflags_flags &= ~CERTOPT_X_FWD;
++	else if (strcasecmp(opt, "permit-x11-forwarding") == 0)
++		certflags_flags |= CERTOPT_X_FWD;
++	else if (strcasecmp(opt, "no-agent-forwarding") == 0)
++		certflags_flags &= ~CERTOPT_AGENT_FWD;
++	else if (strcasecmp(opt, "permit-agent-forwarding") == 0)
++		certflags_flags |= CERTOPT_AGENT_FWD;
++	else if (strcasecmp(opt, "no-port-forwarding") == 0)
++		certflags_flags &= ~CERTOPT_PORT_FWD;
++	else if (strcasecmp(opt, "permit-port-forwarding") == 0)
++		certflags_flags |= CERTOPT_PORT_FWD;
++	else if (strcasecmp(opt, "no-pty") == 0)
++		certflags_flags &= ~CERTOPT_PTY;
++	else if (strcasecmp(opt, "permit-pty") == 0)
++		certflags_flags |= CERTOPT_PTY;
++	else if (strcasecmp(opt, "no-user-rc") == 0)
++		certflags_flags &= ~CERTOPT_USER_RC;
++	else if (strcasecmp(opt, "permit-user-rc") == 0)
++		certflags_flags |= CERTOPT_USER_RC;
++	else if (strncasecmp(opt, "force-command=", 14) == 0) {
++		val = opt + 14;
++		if (*val == '\0')
++			fatal("Empty force-command option");
++		if (certflags_command != NULL)
++			fatal("force-command already specified");
++		certflags_command = xstrdup(val);
++	} else if (strncasecmp(opt, "source-address=", 15) == 0) {
++		val = opt + 15;
++		if (*val == '\0')
++			fatal("Empty source-address option");
++		if (certflags_src_addr != NULL)
++			fatal("source-address already specified");
++		if (addr_match_cidr_list(NULL, val) != 0)
++			fatal("Invalid source-address list");
++		certflags_src_addr = xstrdup(val);
++	} else
++		fatal("Unsupported certificate option \"%s\"", opt);
++}
++
++static void
++do_show_cert(struct passwd *pw)
++{
++	Key *key;
++	struct stat st;
++	char *key_fp, *ca_fp;
++	Buffer options, option;
++	u_char *name, *data;
++	u_int i, dlen, v00;
++
++	if (!have_identity)
++		ask_filename(pw, "Enter file in which the key is");
++	if (stat(identity_file, &st) < 0) {
++		perror(identity_file);
++		exit(1);
++	}
++	if ((key = key_load_public(identity_file, NULL)) == NULL)
++		fatal("%s is not a public key", identity_file);
++	if (!key_is_cert(key))
++		fatal("%s is not a certificate", identity_file);
++	v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00;
++
++	key_fp = key_fingerprint(key, FIPS_mode() ? SSH_FP_SHA1 : SSH_FP_MD5, SSH_FP_HEX);
++	ca_fp = key_fingerprint(key->cert->signature_key,
++	    FIPS_mode() ? SSH_FP_SHA1 : SSH_FP_MD5, SSH_FP_HEX);
++
++	printf("%s:\n", identity_file);
++	printf("        Type: %s %s certificate\n", key_ssh_name(key),
++	    key_cert_type(key));
++	printf("        Public key: %s %s\n", key_type(key), key_fp);
++	printf("        Signing CA: %s %s\n",
++	    key_type(key->cert->signature_key), ca_fp);
++	printf("        Key ID: \"%s\"\n", key->cert->key_id);
++	if (!v00)
++		printf("        Serial: %llu\n", key->cert->serial);
++	printf("        Valid: %s\n",
++	    fmt_validity(key->cert->valid_after, key->cert->valid_before));
++	printf("        Principals: ");
++	if (key->cert->nprincipals == 0)
++		printf("(none)\n");
++	else {
++		for (i = 0; i < key->cert->nprincipals; i++)
++			printf("\n                %s",
++			    key->cert->principals[i]);
++		printf("\n");
++	}
++	printf("        Critical Options: ");
++	if (buffer_len(&key->cert->critical) == 0)
++		printf("(none)\n");
++	else {
++		printf("\n");
++		buffer_init(&options);
++		buffer_append(&options,
++		    buffer_ptr(&key->cert->critical),
++		    buffer_len(&key->cert->critical));
++		buffer_init(&option);
++		while (buffer_len(&options) != 0) {
++			name = buffer_get_string(&options, NULL);
++			data = buffer_get_string_ptr(&options, &dlen);
++			buffer_append(&option, data, dlen);
++			printf("                %s", name);
++			if (strcmp(name, "permit-X11-forwarding") == 0 ||
++			    strcmp(name, "permit-agent-forwarding") == 0 ||
++			    strcmp(name, "permit-port-forwarding") == 0 ||
++			    strcmp(name, "permit-pty") == 0 ||
++			    strcmp(name, "permit-user-rc") == 0)
++				printf("\n");
++			else if (strcmp(name, "force-command") == 0 ||
++			    strcmp(name, "source-address") == 0) {
++				data = buffer_get_string(&option, NULL);
++				printf(" %s\n", data);
++				xfree(data);
++			} else {
++				printf(" UNKNOWN OPTION (len %u)\n",
++				    buffer_len(&option));
++				buffer_clear(&option);
++			}
++			xfree(name);
++			if (buffer_len(&option) != 0)
++				fatal("Option corrupt: extra data at end");
++		}
++		buffer_free(&option);
++		buffer_free(&options);
++	}
++	if (!v00) {
++		printf("        Extensions: ");
++		if (buffer_len(&key->cert->extensions) == 0)
++			printf("(none)\n");
++		else {
++			printf("\n");
++			buffer_init(&options);
++			buffer_append(&options,
++			    buffer_ptr(&key->cert->extensions),
++			    buffer_len(&key->cert->extensions));
++			buffer_init(&option);
++			while (buffer_len(&options) != 0) {
++				name = buffer_get_string(&options, NULL);
++				(void)buffer_get_string_ptr(&options, &dlen);
++				printf("                %s UNKNOWN OPTION "
++				    "(len %u)\n", name, dlen);
++				xfree(name);
++			}
++			buffer_free(&option);
++			buffer_free(&options);
++		}
++	}
++	exit(0);
++}
++
+ static void
+ usage(void)
+ {
+@@ -1053,25 +1545,32 @@ usage(void)
+ 	fprintf(stderr, "  -G file     Generate candidates for DH-GEX moduli.\n");
+ 	fprintf(stderr, "  -g          Use generic DNS resource record format.\n");
+ 	fprintf(stderr, "  -H          Hash names in known_hosts file.\n");
++	fprintf(stderr, "  -h          Generate host certificate instead of a user certificate.\n");
++	fprintf(stderr, "  -I key_id   Key identifier to include in certificate.\n");
+ 	fprintf(stderr, "  -i          Convert RFC 4716 to OpenSSH key file.\n");
++	fprintf(stderr, "  -L          Print the contents of a certificate.\n");
+ 	fprintf(stderr, "  -l          Show fingerprint of key file.\n");
+ #ifdef SMARTCARD
+ 	fprintf(stderr, "  -n          Extract the public key from smartcard.\n");
+ #endif /* SMARTCARD */
+ 	fprintf(stderr, "  -M memory   Amount of memory (MB) to use for generating DH-GEX moduli.\n");
+ 	fprintf(stderr, "  -N phrase   Provide new passphrase.\n");
++	fprintf(stderr, "  -O option   Specify a certificate option.\n");
+ 	fprintf(stderr, "  -P phrase   Provide old passphrase.\n");
+ 	fprintf(stderr, "  -p          Change passphrase of private key file.\n");
+ 	fprintf(stderr, "  -q          Quiet.\n");
+ 	fprintf(stderr, "  -R hostname Remove host from known_hosts file.\n");
+ 	fprintf(stderr, "  -r hostname Print DNS resource record.\n");
+ 	fprintf(stderr, "  -S start    Start point (hex) for generating DH-GEX moduli.\n");
++	fprintf(stderr, "  -s ca_key   Certify keys with CA key.\n");
+ 	fprintf(stderr, "  -T file     Screen candidates for DH-GEX moduli.\n");
+ 	fprintf(stderr, "  -t type     Specify type of key to create.\n");
++	fprintf(stderr, "  -V from:to  Specify certificate validity interval.\n");
+ 	fprintf(stderr, "  -v          Verbose.\n");
+ 	fprintf(stderr, "  -W gen      Generator to use for generating DH-GEX moduli.\n");
+ 	fprintf(stderr, "  -y          Read private key file and print public key.\n");
+-
++	fprintf(stderr, "  -Z name,... User/host principal names to include in certificate\n");
++	fprintf(stderr, "  -z serial   Specify a serial number.\n");
+ 	exit(1);
+ }
+ 
+@@ -1121,7 +1620,7 @@ main(int argc, char **argv)
+ 	}
+ 
+ 	while ((opt = getopt(argc, argv,
+-	    "degiqpclnBHvxXyF:b:f:t:D:N:P:C:r:g:R:T:G:M:S:a:W:")) != -1) {
++	    "degiqpclnBHLhvxXyF:b:f:t:D:I:N:P:O:C:r:g:R:T:G:M:S:Z:s:a:V:W:z:")) != -1) {
+ 		switch (opt) {
+ 		case 'b':
+ 			bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr);
+@@ -1136,16 +1635,25 @@ main(int argc, char **argv)
+ 		case 'H':
+ 			hash_hosts = 1;
+ 			break;
++		case 'I':
++			cert_key_id = optarg;
++			break;
+ 		case 'R':
+ 			delete_host = 1;
+ 			rr_hostname = optarg;
+ 			break;
++		case 'L':
++			show_cert = 1;
++			break;
+ 		case 'l':
+ 			print_fingerprint = 1;
+ 			break;
+ 		case 'B':
+ 			print_bubblebabble = 1;
+ 			break;
++		case 'Z':
++			cert_principals = optarg;
++			break;
+ 		case 'p':
+ 			change_passphrase = 1;
+ 			break;
+@@ -1171,6 +1679,9 @@ main(int argc, char **argv)
+ 		case 'N':
+ 			identity_new_passphrase = optarg;
+ 			break;
++		case 'O':
++			add_cert_option(optarg);
++			break;
+ 		case 'C':
+ 			identity_comment = optarg;
+ 			break;
+@@ -1182,6 +1693,10 @@ main(int argc, char **argv)
+ 			/* export key */
+ 			convert_to_ssh2 = 1;
+ 			break;
++		case 'h':
++			cert_key_type = SSH2_CERT_TYPE_HOST;
++			certflags_flags = 0;
++			break;
+ 		case 'i':
+ 		case 'X':
+ 			/* import key */
+@@ -1193,6 +1708,9 @@ main(int argc, char **argv)
+ 		case 'd':
+ 			key_type_name = "dsa";
+ 			break;
++		case 's':
++			ca_key_path = optarg;
++			break;
+ 		case 't':
+ 			key_type_name = optarg;
+ 			break;
+@@ -1226,9 +1744,8 @@ main(int argc, char **argv)
+ 			break;
+ 		case 'M':
+ 			memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr);
+-			if (errstr) {
++			if (errstr)
+ 				fatal("Memory limit is %s: %s", errstr, optarg);
+-			}
+ 			break;
+ 		case 'G':
+ 			do_gen_candidates = 1;
+@@ -1247,6 +1764,14 @@ main(int argc, char **argv)
+ 			if (BN_hex2bn(&start, optarg) == 0)
+ 				fatal("Invalid start point.");
+ 			break;
++		case 'V':
++			parse_cert_times(optarg);
++			break;
++		case 'z':
++			cert_serial = strtonum(optarg, 0, LLONG_MAX, &errstr);
++			if (errstr)
++				fatal("Invalid serial number: %s", errstr);
++			break;
+ 		case '?':
+ 		default:
+ 			usage();
+@@ -1256,7 +1781,15 @@ main(int argc, char **argv)
+ 	/* reinit */
+ 	log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
+ 
+-	if (optind < argc) {
++	argv += optind;
++	argc -= optind;
++
++	if (ca_key_path != NULL) {
++		if (argc < 1) {
++			printf("Too few arguments.\n");
++			usage();
++		}
++	} else if (argc > 0) {
+ 		printf("Too many arguments.\n");
+ 		usage();
+ 	}
+@@ -1268,6 +1801,13 @@ main(int argc, char **argv)
+ 		printf("Cannot use -l with -D or -R.\n");
+ 		usage();
+ 	}
++	if (ca_key_path != NULL) {
++		if (cert_key_id == NULL)
++			fatal("Must specify key id (-I) when certifying");
++		do_ca_sign(pw, argc, argv);
++	}
++	if (show_cert)
++		do_show_cert(pw);
+ 	if (delete_host || hash_hosts || find_host)
+ 		do_known_hosts(pw, rr_hostname);
+ 	if (print_fingerprint || print_bubblebabble)
+diff -up openssh-5.3p1/ssh-rsa.c.certificates openssh-5.3p1/ssh-rsa.c
+--- openssh-5.3p1/ssh-rsa.c.certificates	2013-07-18 15:38:20.283187078 +0200
++++ openssh-5.3p1/ssh-rsa.c	2013-07-18 15:38:20.450186350 +0200
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: ssh-rsa.c,v 1.39 2006/08/03 03:34:42 deraadt Exp $ */
++/* $OpenBSD: ssh-rsa.c,v 1.41 2010/04/16 01:47:26 djm Exp $ */
+ /*
+  * Copyright (c) 2000, 2003 Markus Friedl <markus at openbsd.org>
+  *
+@@ -50,7 +50,8 @@ ssh_rsa_sign(const Key *key, u_char **si
+ 	int ok, nid;
+ 	Buffer b;
+ 
+-	if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) {
++	if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA &&
++	    key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) {
+ 		error("ssh_rsa_sign: no RSA key");
+ 		return -1;
+ 	}
+@@ -149,7 +150,8 @@ ssh_rsa_verify(const Key *key, const u_c
+ 	u_int len, dlen, modlen;
+ 	int rlen, ret, nid;
+ 
+-	if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) {
++	if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA &&
++	    key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) {
+ 		error("ssh_rsa_verify: no RSA key");
+ 		return -1;
+ 	}
+--- openssh-5.3p1/ssh.c.certificates	2013-09-06 16:56:03.391760101 +0200
++++ openssh-5.3p1/ssh.c	2013-09-06 16:56:03.417759981 +0200
+@@ -1390,8 +1390,30 @@ load_public_identity_files(void)
+ 		xfree(options.identity_files[i]);
+ 		identity_files[n_ids] = filename;
+ 		identity_keys[n_ids] = public;
++
+ 		if (++n_ids >= SSH_MAX_IDENTITY_FILES)
+ 			continue;
++
++		/* Try to add the certificate variant too */
++		xasprintf(&cp, "%s-cert", filename);
++		public = key_load_public(cp, NULL);
++		debug("identity file %s type %d", cp,
++		    public ? public->type : -1);
++		if (public == NULL) {
++			xfree(cp);
++			continue;
++		}
++		if (!key_is_cert(public)) {
++			debug("%s: key %s type %s is not a certificate",
++			    __func__, cp, key_type(public));
++			key_free(public);
++			xfree(cp);
++			continue;
++		}
++		identity_keys[n_ids] = public;
++		/* point to the original path, most likely the private key */
++		identity_files[n_ids] = xstrdup(filename);
++		n_ids++;
+ 	}
+ 	options.num_identity_files = n_ids;
+ 	memcpy(options.identity_files, identity_files, sizeof(identity_files));
diff --git a/openssh-5.3p1-utf8-banner-message.patch b/openssh-5.3p1-utf8-banner-message.patch
new file mode 100644
index 0000000..fe58a30
--- /dev/null
+++ b/openssh-5.3p1-utf8-banner-message.patch
@@ -0,0 +1,1047 @@
+diff --git a/Makefile.in b/Makefile.in
+index d7c75a1..075ac8b 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -78,7 +78,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
+ 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \
+ 	kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \
+ 	entropy.o scard-opensc.o gss-genr.o umac.o jpake.o schnorr.o nsskeys.o \
+-	kexgssc.o auditstub.o ssh-pkcs11.o
++	kexgssc.o auditstub.o ssh-pkcs11.o stringprep.o
+ 
+ SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
+ 	sshconnect.o sshconnect1.o sshconnect2.o mux.o \
+diff --git a/misc.h b/misc.h
+index 5da170d..0f461bf 100644
+--- a/misc.h
++++ b/misc.h
+@@ -90,4 +90,7 @@ char	*read_passphrase(const char *, int);
+ int	 ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
+ int	 read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
+ 
++/* stringprep.c */
++int ssh_utf8_stringprep(const char *, char *, size_t);
++
+ #endif /* _MISC_H */
+diff --git a/sshconnect2.c b/sshconnect2.c
+index 0091788..011d67c 100644
+--- a/sshconnect2.c
++++ b/sshconnect2.c
+@@ -43,6 +43,8 @@
+ #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H)
+ #include <vis.h>
+ #endif
++#include <locale.h>
++#include <langinfo.h>
+ 
+ #include <openssl/fips.h>
+ 
+@@ -472,21 +474,51 @@ input_userauth_error(int type, u_int32_t seq, void *ctxt)
+ 	    "type %d", type);
+ }
+ 
++/* Check whether we can display UTF-8 safely */
++static int
++utf8_ok(void)
++{
++	static int ret = -1;
++	char *cp;
++
++	if (ret == -1) {
++		setlocale(LC_CTYPE, "");
++		cp = nl_langinfo(CODESET);
++		ret = strcmp(cp, "UTF-8") == 0;
++	}
++	return ret;
++}
++
+ /* ARGSUSED */
+ void
+ input_userauth_banner(int type, u_int32_t seq, void *ctxt)
+ {
+ 	char *msg, *raw, *lang;
+-	u_int len;
++	u_int done, len;
+ 
+ 	debug3("input_userauth_banner");
++
+ 	raw = packet_get_string(&len);
+ 	lang = packet_get_string(NULL);
+ 	if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) {
+ 		if (len > 65536)
+ 			len = 65536;
+ 		msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */
+-		strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL|VIS_NOSLASH);
++		done = 0;
++		if (utf8_ok()) {
++			if (ssh_utf8_stringprep(raw, msg, len * 4 + 1) == 0)
++				done = 1;
++			else
++				debug2("%s: UTF8 stringprep failed", __func__);
++		}
++		/*
++		 * Fallback to strnvis if UTF8 display not supported or
++		 * conversion failed.
++		 */
++		if (!done) {
++			strnvis(msg, raw, len * 4 + 1,
++			    VIS_SAFE|VIS_OCTAL|VIS_NOSLASH);
++		}
+ 		fprintf(stderr, "%s", msg);
+ 		xfree(msg);
+ 	}
+diff --git a/stringprep.c b/stringprep.c
+new file mode 100644
+index 0000000..73876b0
+--- /dev/null
++++ b/stringprep.c
+@@ -0,0 +1,949 @@
++/*
++ * Copyright (c) 2013 Damien Miller <djm at mindrot.org>
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++/*
++ * This is a simple RFC3454 stringprep profile to sanitise UTF-8 strings
++ * from untrusted sources.
++ *
++ * It is intended to be used prior to display of untrusted strings only.
++ * It should not be used for logging because of bi-di ambiguity. It
++ * should also not be used in any case where lack of normalisation may
++ * cause problems.
++ *
++ * This profile uses the prohibition and mapping tables from RFC3454
++ * (listed below) but the unassigned character table has been updated to
++ * Unicode 6.2. It uses a local whitelist of whitespace characters (\n,
++ * \a and \t). Unicode normalisation and bi-di testing are not used.
++ *
++ * XXX: implement bi-di handling (needed for logs)
++ * XXX: implement KC normalisation (needed for passing to libs/syscalls)
++ */
++
++#include <sys/types.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <limits.h>
++#include <ctype.h>
++
++#include "misc.h"
++
++#define BANNED_RFC3629 0
++#define UTF8_DEBUG 0
++
++#if UTF8_DEBUG
++# define DBG(x) \
++	do { \
++		printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \
++		printf x; \
++		printf("\n"); \
++		fflush(stdout); \
++	} while (0)
++#else
++# define DBG(x)
++#endif
++
++struct u32_range {
++	u_int32_t lo, hi;  /* Inclusive */
++};
++
++/* RFC3454 Table A.1 */
++static const struct u32_range unassigned[] = {
++	{ 0x0378, 0x0379 },
++	{ 0x037F, 0x0383 },
++	{ 0x038B, 0x038B },
++	{ 0x038D, 0x038D },
++	{ 0x03A2, 0x03A2 },
++	{ 0x0528, 0x0530 },
++	{ 0x0557, 0x0558 },
++	{ 0x0560, 0x0560 },
++	{ 0x0588, 0x0588 },
++	{ 0x058B, 0x058E },
++	{ 0x0590, 0x0590 },
++	{ 0x05C8, 0x05CF },
++	{ 0x05EB, 0x05EF },
++	{ 0x05F5, 0x05FF },
++	{ 0x0605, 0x0605 },
++	{ 0x061C, 0x061D },
++	{ 0x070E, 0x070E },
++	{ 0x074B, 0x074C },
++	{ 0x07B2, 0x07BF },
++	{ 0x07FB, 0x07FF },
++	{ 0x082E, 0x082F },
++	{ 0x083F, 0x083F },
++	{ 0x085C, 0x085D },
++	{ 0x085F, 0x089F },
++	{ 0x08A1, 0x08A1 },
++	{ 0x08AD, 0x08E3 },
++	{ 0x08FF, 0x08FF },
++	{ 0x0978, 0x0978 },
++	{ 0x0980, 0x0980 },
++	{ 0x0984, 0x0984 },
++	{ 0x098D, 0x098E },
++	{ 0x0991, 0x0992 },
++	{ 0x09A9, 0x09A9 },
++	{ 0x09B1, 0x09B1 },
++	{ 0x09B3, 0x09B5 },
++	{ 0x09BA, 0x09BB },
++	{ 0x09C5, 0x09C6 },
++	{ 0x09C9, 0x09CA },
++	{ 0x09CF, 0x09D6 },
++	{ 0x09D8, 0x09DB },
++	{ 0x09DE, 0x09DE },
++	{ 0x09E4, 0x09E5 },
++	{ 0x09FC, 0x0A00 },
++	{ 0x0A04, 0x0A04 },
++	{ 0x0A0B, 0x0A0E },
++	{ 0x0A11, 0x0A12 },
++	{ 0x0A29, 0x0A29 },
++	{ 0x0A31, 0x0A31 },
++	{ 0x0A34, 0x0A34 },
++	{ 0x0A37, 0x0A37 },
++	{ 0x0A3A, 0x0A3B },
++	{ 0x0A3D, 0x0A3D },
++	{ 0x0A43, 0x0A46 },
++	{ 0x0A49, 0x0A4A },
++	{ 0x0A4E, 0x0A50 },
++	{ 0x0A52, 0x0A58 },
++	{ 0x0A5D, 0x0A5D },
++	{ 0x0A5F, 0x0A65 },
++	{ 0x0A76, 0x0A80 },
++	{ 0x0A84, 0x0A84 },
++	{ 0x0A8E, 0x0A8E },
++	{ 0x0A92, 0x0A92 },
++	{ 0x0AA9, 0x0AA9 },
++	{ 0x0AB1, 0x0AB1 },
++	{ 0x0AB4, 0x0AB4 },
++	{ 0x0ABA, 0x0ABB },
++	{ 0x0AC6, 0x0AC6 },
++	{ 0x0ACA, 0x0ACA },
++	{ 0x0ACE, 0x0ACF },
++	{ 0x0AD1, 0x0ADF },
++	{ 0x0AE4, 0x0AE5 },
++	{ 0x0AF2, 0x0B00 },
++	{ 0x0B04, 0x0B04 },
++	{ 0x0B0D, 0x0B0E },
++	{ 0x0B11, 0x0B12 },
++	{ 0x0B29, 0x0B29 },
++	{ 0x0B31, 0x0B31 },
++	{ 0x0B34, 0x0B34 },
++	{ 0x0B3A, 0x0B3B },
++	{ 0x0B45, 0x0B46 },
++	{ 0x0B49, 0x0B4A },
++	{ 0x0B4E, 0x0B55 },
++	{ 0x0B58, 0x0B5B },
++	{ 0x0B5E, 0x0B5E },
++	{ 0x0B64, 0x0B65 },
++	{ 0x0B78, 0x0B81 },
++	{ 0x0B84, 0x0B84 },
++	{ 0x0B8B, 0x0B8D },
++	{ 0x0B91, 0x0B91 },
++	{ 0x0B96, 0x0B98 },
++	{ 0x0B9B, 0x0B9B },
++	{ 0x0B9D, 0x0B9D },
++	{ 0x0BA0, 0x0BA2 },
++	{ 0x0BA5, 0x0BA7 },
++	{ 0x0BAB, 0x0BAD },
++	{ 0x0BBA, 0x0BBD },
++	{ 0x0BC3, 0x0BC5 },
++	{ 0x0BC9, 0x0BC9 },
++	{ 0x0BCE, 0x0BCF },
++	{ 0x0BD1, 0x0BD6 },
++	{ 0x0BD8, 0x0BE5 },
++	{ 0x0BFB, 0x0C00 },
++	{ 0x0C04, 0x0C04 },
++	{ 0x0C0D, 0x0C0D },
++	{ 0x0C11, 0x0C11 },
++	{ 0x0C29, 0x0C29 },
++	{ 0x0C34, 0x0C34 },
++	{ 0x0C3A, 0x0C3C },
++	{ 0x0C45, 0x0C45 },
++	{ 0x0C49, 0x0C49 },
++	{ 0x0C4E, 0x0C54 },
++	{ 0x0C57, 0x0C57 },
++	{ 0x0C5A, 0x0C5F },
++	{ 0x0C64, 0x0C65 },
++	{ 0x0C70, 0x0C77 },
++	{ 0x0C80, 0x0C81 },
++	{ 0x0C84, 0x0C84 },
++	{ 0x0C8D, 0x0C8D },
++	{ 0x0C91, 0x0C91 },
++	{ 0x0CA9, 0x0CA9 },
++	{ 0x0CB4, 0x0CB4 },
++	{ 0x0CBA, 0x0CBB },
++	{ 0x0CC5, 0x0CC5 },
++	{ 0x0CC9, 0x0CC9 },
++	{ 0x0CCE, 0x0CD4 },
++	{ 0x0CD7, 0x0CDD },
++	{ 0x0CDF, 0x0CDF },
++	{ 0x0CE4, 0x0CE5 },
++	{ 0x0CF0, 0x0CF0 },
++	{ 0x0CF3, 0x0D01 },
++	{ 0x0D04, 0x0D04 },
++	{ 0x0D0D, 0x0D0D },
++	{ 0x0D11, 0x0D11 },
++	{ 0x0D3B, 0x0D3C },
++	{ 0x0D45, 0x0D45 },
++	{ 0x0D49, 0x0D49 },
++	{ 0x0D4F, 0x0D56 },
++	{ 0x0D58, 0x0D5F },
++	{ 0x0D64, 0x0D65 },
++	{ 0x0D76, 0x0D78 },
++	{ 0x0D80, 0x0D81 },
++	{ 0x0D84, 0x0D84 },
++	{ 0x0D97, 0x0D99 },
++	{ 0x0DB2, 0x0DB2 },
++	{ 0x0DBC, 0x0DBC },
++	{ 0x0DBE, 0x0DBF },
++	{ 0x0DC7, 0x0DC9 },
++	{ 0x0DCB, 0x0DCE },
++	{ 0x0DD5, 0x0DD5 },
++	{ 0x0DD7, 0x0DD7 },
++	{ 0x0DE0, 0x0DF1 },
++	{ 0x0DF5, 0x0E00 },
++	{ 0x0E3B, 0x0E3E },
++	{ 0x0E5C, 0x0E80 },
++	{ 0x0E83, 0x0E83 },
++	{ 0x0E85, 0x0E86 },
++	{ 0x0E89, 0x0E89 },
++	{ 0x0E8B, 0x0E8C },
++	{ 0x0E8E, 0x0E93 },
++	{ 0x0E98, 0x0E98 },
++	{ 0x0EA0, 0x0EA0 },
++	{ 0x0EA4, 0x0EA4 },
++	{ 0x0EA6, 0x0EA6 },
++	{ 0x0EA8, 0x0EA9 },
++	{ 0x0EAC, 0x0EAC },
++	{ 0x0EBA, 0x0EBA },
++	{ 0x0EBE, 0x0EBF },
++	{ 0x0EC5, 0x0EC5 },
++	{ 0x0EC7, 0x0EC7 },
++	{ 0x0ECE, 0x0ECF },
++	{ 0x0EDA, 0x0EDB },
++	{ 0x0EE0, 0x0EFF },
++	{ 0x0F48, 0x0F48 },
++	{ 0x0F6D, 0x0F70 },
++	{ 0x0F98, 0x0F98 },
++	{ 0x0FBD, 0x0FBD },
++	{ 0x0FCD, 0x0FCD },
++	{ 0x0FDB, 0x0FFF },
++	{ 0x10C6, 0x10C6 },
++	{ 0x10C8, 0x10CC },
++	{ 0x10CE, 0x10CF },
++	{ 0x1249, 0x1249 },
++	{ 0x124E, 0x124F },
++	{ 0x1257, 0x1257 },
++	{ 0x1259, 0x1259 },
++	{ 0x125E, 0x125F },
++	{ 0x1289, 0x1289 },
++	{ 0x128E, 0x128F },
++	{ 0x12B1, 0x12B1 },
++	{ 0x12B6, 0x12B7 },
++	{ 0x12BF, 0x12BF },
++	{ 0x12C1, 0x12C1 },
++	{ 0x12C6, 0x12C7 },
++	{ 0x12D7, 0x12D7 },
++	{ 0x1311, 0x1311 },
++	{ 0x1316, 0x1317 },
++	{ 0x135B, 0x135C },
++	{ 0x137D, 0x137F },
++	{ 0x139A, 0x139F },
++	{ 0x13F5, 0x13FF },
++	{ 0x169D, 0x169F },
++	{ 0x16F1, 0x16FF },
++	{ 0x170D, 0x170D },
++	{ 0x1715, 0x171F },
++	{ 0x1737, 0x173F },
++	{ 0x1754, 0x175F },
++	{ 0x176D, 0x176D },
++	{ 0x1771, 0x1771 },
++	{ 0x1774, 0x177F },
++	{ 0x17DE, 0x17DF },
++	{ 0x17EA, 0x17EF },
++	{ 0x17FA, 0x17FF },
++	{ 0x180F, 0x180F },
++	{ 0x181A, 0x181F },
++	{ 0x1878, 0x187F },
++	{ 0x18AB, 0x18AF },
++	{ 0x18F6, 0x18FF },
++	{ 0x191D, 0x191F },
++	{ 0x192C, 0x192F },
++	{ 0x193C, 0x193F },
++	{ 0x1941, 0x1943 },
++	{ 0x196E, 0x196F },
++	{ 0x1975, 0x197F },
++	{ 0x19AC, 0x19AF },
++	{ 0x19CA, 0x19CF },
++	{ 0x19DB, 0x19DD },
++	{ 0x1A1C, 0x1A1D },
++	{ 0x1A5F, 0x1A5F },
++	{ 0x1A7D, 0x1A7E },
++	{ 0x1A8A, 0x1A8F },
++	{ 0x1A9A, 0x1A9F },
++	{ 0x1AAE, 0x1AFF },
++	{ 0x1B4C, 0x1B4F },
++	{ 0x1B7D, 0x1B7F },
++	{ 0x1BF4, 0x1BFB },
++	{ 0x1C38, 0x1C3A },
++	{ 0x1C4A, 0x1C4C },
++	{ 0x1C80, 0x1CBF },
++	{ 0x1CC8, 0x1CCF },
++	{ 0x1CF7, 0x1CFF },
++	{ 0x1DE7, 0x1DFB },
++	{ 0x1F16, 0x1F17 },
++	{ 0x1F1E, 0x1F1F },
++	{ 0x1F46, 0x1F47 },
++	{ 0x1F4E, 0x1F4F },
++	{ 0x1F58, 0x1F58 },
++	{ 0x1F5A, 0x1F5A },
++	{ 0x1F5C, 0x1F5C },
++	{ 0x1F5E, 0x1F5E },
++	{ 0x1F7E, 0x1F7F },
++	{ 0x1FB5, 0x1FB5 },
++	{ 0x1FC5, 0x1FC5 },
++	{ 0x1FD4, 0x1FD5 },
++	{ 0x1FDC, 0x1FDC },
++	{ 0x1FF0, 0x1FF1 },
++	{ 0x1FF5, 0x1FF5 },
++	{ 0x1FFF, 0x1FFF },
++	{ 0x2065, 0x2069 },
++	{ 0x2072, 0x2073 },
++	{ 0x208F, 0x208F },
++	{ 0x209D, 0x209F },
++	{ 0x20BB, 0x20CF },
++	{ 0x20F1, 0x20FF },
++	{ 0x218A, 0x218F },
++	{ 0x23F4, 0x23FF },
++	{ 0x2427, 0x243F },
++	{ 0x244B, 0x245F },
++	{ 0x2700, 0x2700 },
++	{ 0x2B4D, 0x2B4F },
++	{ 0x2B5A, 0x2BFF },
++	{ 0x2C2F, 0x2C2F },
++	{ 0x2C5F, 0x2C5F },
++	{ 0x2CF4, 0x2CF8 },
++	{ 0x2D26, 0x2D26 },
++	{ 0x2D28, 0x2D2C },
++	{ 0x2D2E, 0x2D2F },
++	{ 0x2D68, 0x2D6E },
++	{ 0x2D71, 0x2D7E },
++	{ 0x2D97, 0x2D9F },
++	{ 0x2DA7, 0x2DA7 },
++	{ 0x2DAF, 0x2DAF },
++	{ 0x2DB7, 0x2DB7 },
++	{ 0x2DBF, 0x2DBF },
++	{ 0x2DC7, 0x2DC7 },
++	{ 0x2DCF, 0x2DCF },
++	{ 0x2DD7, 0x2DD7 },
++	{ 0x2DDF, 0x2DDF },
++	{ 0x2E3C, 0x2E7F },
++	{ 0x2E9A, 0x2E9A },
++	{ 0x2EF4, 0x2EFF },
++	{ 0x2FD6, 0x2FEF },
++	{ 0x2FFC, 0x2FFF },
++	{ 0x3040, 0x3040 },
++	{ 0x3097, 0x3098 },
++	{ 0x3100, 0x3104 },
++	{ 0x312E, 0x3130 },
++	{ 0x318F, 0x318F },
++	{ 0x31BB, 0x31BF },
++	{ 0x31E4, 0x31EF },
++	{ 0x321F, 0x321F },
++	{ 0x32FF, 0x32FF },
++	{ 0x4DB6, 0x4DBF },
++	{ 0x9FA6, 0x9FCB },
++	{ 0x9FCD, 0x9FFF },
++	{ 0xA48D, 0xA48F },
++	{ 0xA4C7, 0xA4CF },
++	{ 0xA62C, 0xA63F },
++	{ 0xA698, 0xA69E },
++	{ 0xA6F8, 0xA6FF },
++	{ 0xA78F, 0xA78F },
++	{ 0xA794, 0xA79F },
++	{ 0xA7AB, 0xA7F7 },
++	{ 0xA82C, 0xA82F },
++	{ 0xA83A, 0xA83F },
++	{ 0xA878, 0xA87F },
++	{ 0xA8C5, 0xA8CD },
++	{ 0xA8DA, 0xA8DF },
++	{ 0xA8FC, 0xA8FF },
++	{ 0xA954, 0xA95E },
++	{ 0xA97D, 0xA97F },
++	{ 0xA9CE, 0xA9CE },
++	{ 0xA9DA, 0xA9DD },
++	{ 0xA9E0, 0xA9FF },
++	{ 0xAA37, 0xAA3F },
++	{ 0xAA4E, 0xAA4F },
++	{ 0xAA5A, 0xAA5B },
++	{ 0xAA7C, 0xAA7F },
++	{ 0xAAC3, 0xAADA },
++	{ 0xAAF7, 0xAB00 },
++	{ 0xAB07, 0xAB08 },
++	{ 0xAB0F, 0xAB10 },
++	{ 0xAB17, 0xAB1F },
++	{ 0xAB27, 0xAB27 },
++	{ 0xAB2F, 0xABBF },
++	{ 0xABEE, 0xABEF },
++	{ 0xABFA, 0xABFF },
++	{ 0xD7A4, 0xD7AF },
++	{ 0xD7C7, 0xD7CA },
++	{ 0xD7FC, 0xD7FF },
++	{ 0xFA6E, 0xFA6F },
++	{ 0xFADA, 0xFAFF },
++	{ 0xFB07, 0xFB12 },
++	{ 0xFB18, 0xFB1C },
++	{ 0xFB37, 0xFB37 },
++	{ 0xFB3D, 0xFB3D },
++	{ 0xFB3F, 0xFB3F },
++	{ 0xFB42, 0xFB42 },
++	{ 0xFB45, 0xFB45 },
++	{ 0xFBC2, 0xFBD2 },
++	{ 0xFD40, 0xFD4F },
++	{ 0xFD90, 0xFD91 },
++	{ 0xFDC8, 0xFDCF },
++	{ 0xFDFE, 0xFDFF },
++	{ 0xFE1A, 0xFE1F },
++	{ 0xFE27, 0xFE2F },
++	{ 0xFE53, 0xFE53 },
++	{ 0xFE67, 0xFE67 },
++	{ 0xFE6C, 0xFE6F },
++	{ 0xFE75, 0xFE75 },
++	{ 0xFEFD, 0xFEFE },
++	{ 0xFF00, 0xFF00 },
++	{ 0xFFBF, 0xFFC1 },
++	{ 0xFFC8, 0xFFC9 },
++	{ 0xFFD0, 0xFFD1 },
++	{ 0xFFD8, 0xFFD9 },
++	{ 0xFFDD, 0xFFDF },
++	{ 0xFFE7, 0xFFE7 },
++	{ 0xFFEF, 0xFFF8 },
++	{ 0x1000C, 0x1000C },
++	{ 0x10027, 0x10027 },
++	{ 0x1003B, 0x1003B },
++	{ 0x1003E, 0x1003E },
++	{ 0x1004E, 0x1004F },
++	{ 0x1005E, 0x1007F },
++	{ 0x100FB, 0x100FF },
++	{ 0x10103, 0x10106 },
++	{ 0x10134, 0x10136 },
++	{ 0x1018B, 0x1018F },
++	{ 0x1019C, 0x101CF },
++	{ 0x101FE, 0x1027F },
++	{ 0x1029D, 0x1029F },
++	{ 0x102D1, 0x102FF },
++	{ 0x1031F, 0x1031F },
++	{ 0x10324, 0x1032F },
++	{ 0x1034B, 0x1037F },
++	{ 0x1039E, 0x1039E },
++	{ 0x103C4, 0x103C7 },
++	{ 0x103D6, 0x103FF },
++	{ 0x1049E, 0x1049F },
++	{ 0x104AA, 0x107FF },
++	{ 0x10806, 0x10807 },
++	{ 0x10809, 0x10809 },
++	{ 0x10836, 0x10836 },
++	{ 0x10839, 0x1083B },
++	{ 0x1083D, 0x1083E },
++	{ 0x10856, 0x10856 },
++	{ 0x10860, 0x108FF },
++	{ 0x1091C, 0x1091E },
++	{ 0x1093A, 0x1093E },
++	{ 0x10940, 0x1097F },
++	{ 0x109B8, 0x109BD },
++	{ 0x109C0, 0x109FF },
++	{ 0x10A04, 0x10A04 },
++	{ 0x10A07, 0x10A0B },
++	{ 0x10A14, 0x10A14 },
++	{ 0x10A18, 0x10A18 },
++	{ 0x10A34, 0x10A37 },
++	{ 0x10A3B, 0x10A3E },
++	{ 0x10A48, 0x10A4F },
++	{ 0x10A59, 0x10A5F },
++	{ 0x10A80, 0x10AFF },
++	{ 0x10B36, 0x10B38 },
++	{ 0x10B56, 0x10B57 },
++	{ 0x10B73, 0x10B77 },
++	{ 0x10B80, 0x10BFF },
++	{ 0x10C49, 0x10E5F },
++	{ 0x10E7F, 0x10FFF },
++	{ 0x1104E, 0x11051 },
++	{ 0x11070, 0x1107F },
++	{ 0x110C2, 0x110CF },
++	{ 0x110E9, 0x110EF },
++	{ 0x110FA, 0x110FF },
++	{ 0x11135, 0x11135 },
++	{ 0x11144, 0x1117F },
++	{ 0x111C9, 0x111CF },
++	{ 0x111DA, 0x1167F },
++	{ 0x116B8, 0x116BF },
++	{ 0x116CA, 0x11FFF },
++	{ 0x1236F, 0x123FF },
++	{ 0x12463, 0x1246F },
++	{ 0x12474, 0x12FFF },
++	{ 0x1342F, 0x167FF },
++	{ 0x16A39, 0x16EFF },
++	{ 0x16F45, 0x16F4F },
++	{ 0x16F7F, 0x16F8E },
++	{ 0x16FA0, 0x1AFFF },
++	{ 0x1B002, 0x1CFFF },
++	{ 0x1D0F6, 0x1D0FF },
++	{ 0x1D127, 0x1D128 },
++	{ 0x1D1DE, 0x1D1FF },
++	{ 0x1D246, 0x1D2FF },
++	{ 0x1D357, 0x1D35F },
++	{ 0x1D372, 0x1D3FF },
++	{ 0x1D455, 0x1D455 },
++	{ 0x1D49D, 0x1D49D },
++	{ 0x1D4A0, 0x1D4A1 },
++	{ 0x1D4A3, 0x1D4A4 },
++	{ 0x1D4A7, 0x1D4A8 },
++	{ 0x1D4AD, 0x1D4AD },
++	{ 0x1D4BA, 0x1D4BA },
++	{ 0x1D4BC, 0x1D4BC },
++	{ 0x1D4C4, 0x1D4C4 },
++	{ 0x1D506, 0x1D506 },
++	{ 0x1D50B, 0x1D50C },
++	{ 0x1D515, 0x1D515 },
++	{ 0x1D51D, 0x1D51D },
++	{ 0x1D53A, 0x1D53A },
++	{ 0x1D53F, 0x1D53F },
++	{ 0x1D545, 0x1D545 },
++	{ 0x1D547, 0x1D549 },
++	{ 0x1D551, 0x1D551 },
++	{ 0x1D6A6, 0x1D6A7 },
++	{ 0x1D7CC, 0x1D7CD },
++	{ 0x1D800, 0x1EDFF },
++	{ 0x1EE04, 0x1EE04 },
++	{ 0x1EE20, 0x1EE20 },
++	{ 0x1EE23, 0x1EE23 },
++	{ 0x1EE25, 0x1EE26 },
++	{ 0x1EE28, 0x1EE28 },
++	{ 0x1EE33, 0x1EE33 },
++	{ 0x1EE38, 0x1EE38 },
++	{ 0x1EE3A, 0x1EE3A },
++	{ 0x1EE3C, 0x1EE41 },
++	{ 0x1EE43, 0x1EE46 },
++	{ 0x1EE48, 0x1EE48 },
++	{ 0x1EE4A, 0x1EE4A },
++	{ 0x1EE4C, 0x1EE4C },
++	{ 0x1EE50, 0x1EE50 },
++	{ 0x1EE53, 0x1EE53 },
++	{ 0x1EE55, 0x1EE56 },
++	{ 0x1EE58, 0x1EE58 },
++	{ 0x1EE5A, 0x1EE5A },
++	{ 0x1EE5C, 0x1EE5C },
++	{ 0x1EE5E, 0x1EE5E },
++	{ 0x1EE60, 0x1EE60 },
++	{ 0x1EE63, 0x1EE63 },
++	{ 0x1EE65, 0x1EE66 },
++	{ 0x1EE6B, 0x1EE6B },
++	{ 0x1EE73, 0x1EE73 },
++	{ 0x1EE78, 0x1EE78 },
++	{ 0x1EE7D, 0x1EE7D },
++	{ 0x1EE7F, 0x1EE7F },
++	{ 0x1EE8A, 0x1EE8A },
++	{ 0x1EE9C, 0x1EEA0 },
++	{ 0x1EEA4, 0x1EEA4 },
++	{ 0x1EEAA, 0x1EEAA },
++	{ 0x1EEBC, 0x1EEEF },
++	{ 0x1EEF2, 0x1EFFF },
++	{ 0x1F02C, 0x1F02F },
++	{ 0x1F094, 0x1F09F },
++	{ 0x1F0AF, 0x1F0B0 },
++	{ 0x1F0BF, 0x1F0C0 },
++	{ 0x1F0D0, 0x1F0D0 },
++	{ 0x1F0E0, 0x1F0FF },
++	{ 0x1F10B, 0x1F10F },
++	{ 0x1F12F, 0x1F12F },
++	{ 0x1F16C, 0x1F16F },
++	{ 0x1F19B, 0x1F1E5 },
++	{ 0x1F203, 0x1F20F },
++	{ 0x1F23B, 0x1F23F },
++	{ 0x1F249, 0x1F24F },
++	{ 0x1F252, 0x1F2FF },
++	{ 0x1F321, 0x1F32F },
++	{ 0x1F336, 0x1F336 },
++	{ 0x1F37D, 0x1F37F },
++	{ 0x1F394, 0x1F39F },
++	{ 0x1F3C5, 0x1F3C5 },
++	{ 0x1F3CB, 0x1F3DF },
++	{ 0x1F3F1, 0x1F3FF },
++	{ 0x1F43F, 0x1F43F },
++	{ 0x1F441, 0x1F441 },
++	{ 0x1F4F8, 0x1F4F8 },
++	{ 0x1F4FD, 0x1F4FF },
++	{ 0x1F53E, 0x1F53F },
++	{ 0x1F544, 0x1F54F },
++	{ 0x1F568, 0x1F5FA },
++	{ 0x1F641, 0x1F644 },
++	{ 0x1F650, 0x1F67F },
++	{ 0x1F6C6, 0x1F6FF },
++	{ 0x1F774, 0x1FFFD },
++	{ 0x2A6D7, 0x2A6FF },
++	{ 0x2A701, 0x2B733 },
++	{ 0x2B735, 0x2B73F },
++	{ 0x2B741, 0x2B81C },
++	{ 0x2B81E, 0x2F7FF },
++	{ 0x2FA1E, 0x2FFFD },
++	{ 0x30000, 0x3FFFD },
++	{ 0x40000, 0x4FFFD },
++	{ 0x50000, 0x5FFFD },
++	{ 0x60000, 0x6FFFD },
++	{ 0x70000, 0x7FFFD },
++	{ 0x80000, 0x8FFFD },
++	{ 0x90000, 0x9FFFD },
++	{ 0xA0000, 0xAFFFD },
++	{ 0xB0000, 0xBFFFD },
++	{ 0xC0000, 0xCFFFD },
++	{ 0xD0000, 0xDFFFD },
++	{ 0xE0000, 0xE0000 },
++	{ 0xE0002, 0xE001F },
++	{ 0xE0080, 0xE00FF },
++	{ 0xE01F0, 0xEFFFD },
++};
++
++/* RFC3454 Table B.1 */
++static const struct u32_range map_to_nothing[] = {
++	{ 0x00AD, 0x00AD },
++	{ 0x034F, 0x034F },
++	{ 0x1806, 0x1806 },
++	{ 0x180B, 0x180D },
++	{ 0x200B, 0x200D },
++	{ 0x2060, 0x2060 },
++	{ 0xFE00, 0xFE0F },
++	{ 0xFEFF, 0xFEFF },
++};
++
++/* Local: allow tab, CR and LF */
++static const struct u32_range whitelist[] = {
++	{ 0x09, 0x00 },
++	{ 0x0a, 0x0a },
++	{ 0x0d, 0x0d },
++};
++
++/* RFC3454 Tables in appendix C */
++static const struct u32_range prohibited[] = {
++	/* C.2.1 ASCII control characters */
++	{ 0x0000, 0x001F },
++	{ 0x007F, 0x007F },
++	/* C.2.2 Non-ASCII control characters */
++	{ 0x0080, 0x009F },
++	{ 0x06DD, 0x06DD },
++	{ 0x070F, 0x070F },
++	{ 0x180E, 0x180E },
++	{ 0x200C, 0x200C },
++	{ 0x200D, 0x200D },
++	{ 0x2028, 0x2028 },
++	{ 0x2029, 0x2029 },
++	{ 0x2060, 0x2060 },
++	{ 0x2061, 0x2061 },
++	{ 0x2062, 0x2062 },
++	{ 0x2063, 0x2063 },
++	{ 0x206A, 0x206F },
++	{ 0xFEFF, 0xFEFF },
++	{ 0xFFF9, 0xFFFC },
++	{ 0x1D173, 0x1D17A },
++	/* C.3 Private use */
++	{ 0xE000, 0xF8FF },
++	{ 0xF0000, 0xFFFFD },
++	{ 0x100000, 0x10FFFD },
++	/* C.4 Non-character code points */
++	{ 0xFDD0, 0xFDEF },
++	{ 0xFFFE, 0xFFFF },
++	{ 0x1FFFE, 0x1FFFF },
++	{ 0x2FFFE, 0x2FFFF },
++	{ 0x3FFFE, 0x3FFFF },
++	{ 0x4FFFE, 0x4FFFF },
++	{ 0x5FFFE, 0x5FFFF },
++	{ 0x6FFFE, 0x6FFFF },
++	{ 0x7FFFE, 0x7FFFF },
++	{ 0x8FFFE, 0x8FFFF },
++	{ 0x9FFFE, 0x9FFFF },
++	{ 0xAFFFE, 0xAFFFF },
++	{ 0xBFFFE, 0xBFFFF },
++	{ 0xCFFFE, 0xCFFFF },
++	{ 0xDFFFE, 0xDFFFF },
++	{ 0xEFFFE, 0xEFFFF },
++	{ 0xFFFFE, 0xFFFFF },
++	{ 0x10FFFE, 0x10FFFF },
++	/* C.5 Surrogate codes */
++	{ 0xD800, 0xDFFF },
++	/* C.6 Inappropriate for plain text */
++	{ 0xFFF9, 0xFFF9 },
++	{ 0xFFFA, 0xFFFA },
++	{ 0xFFFB, 0xFFFB },
++	{ 0xFFFC, 0xFFFC },
++	{ 0xFFFD, 0xFFFD },
++	/* C.7 Inappropriate for canonical representation */
++	{ 0x2FF0, 0x2FFB },
++	/* C.8 Change display properties or are deprecated */
++	{ 0x0340, 0x0340 },
++	{ 0x0341, 0x0341 },
++	{ 0x200E, 0x200E },
++	{ 0x200F, 0x200F },
++	{ 0x202A, 0x202A },
++	{ 0x202B, 0x202B },
++	{ 0x202C, 0x202C },
++	{ 0x202D, 0x202D },
++	{ 0x202E, 0x202E },
++	{ 0x206A, 0x206A },
++	{ 0x206B, 0x206B },
++	{ 0x206C, 0x206C },
++	{ 0x206D, 0x206D },
++	{ 0x206E, 0x206E },
++	{ 0x206F, 0x206F },
++	/* C.9 Tagging characters */
++	{ 0xE0001, 0xE0001 },
++	{ 0xE0020, 0xE007F },
++};
++
++static int
++encode_utf8(u_int32_t c, char *s, size_t slen)
++{
++	DBG(("encode CP 0x%x", c));
++	if (c < 0x80) {
++		if (slen >= 1) {
++			s[0] = (char)c;
++		}
++		DBG(("CP 0x%x => %02x", c, (u_char)s[0]));
++		return 1;
++	} else if (c < 0x800) {
++		if (slen >= 2) {
++			s[0] = (char)(0xc0 | (c >> 6));
++			s[1] = (char)(0x80 | (c & 0x3f));
++		}
++		DBG(("CP 0x%x => %02x %02x", c, (u_char)s[0], (u_char)s[1]));
++		return 2;
++	} else if (c < 0x10000) {
++		if (slen >= 3) {
++			s[0] = (char)(0xe0 | (c >> 12));
++			s[1] = (char)(0x80 | ((c >> 6) & 0x3f));
++			s[2] = (char)(0x80 | (c & 0x3f));
++		}
++		DBG(("CP 0x%x => %02x %02x %02x", c, s[0], s[1], s[2]));
++		return 3;
++	} else if (c < 0x200000) {
++		if (slen >= 4) {
++			s[0] = (char)(0xf0 | (c >> 18));
++			s[1] = (char)(0x80 | ((c >> 12) & 0x3f));
++			s[2] = (char)(0x80 | ((c >> 6) & 0x3f));
++			s[3] = (char)(0x80 | (c & 0x3f));
++		}
++		DBG(("CP 0x%x => %02x %02x %02x %02x", c,
++		    s[0], s[1], s[2], s[3]));
++		return 4;
++#if BANED_RFC3629
++	} else if (c < 0x4000000) {
++		if (slen >= 5) {
++			s[0] = (char)(0xf8 | (c >> 24));
++			s[1] = (char)(0x80 | ((c >> 18) & 0x3f));
++			s[2] = (char)(0x80 | ((c >> 12) & 0x3f));
++			s[3] = (char)(0x80 | ((c >> 6) & 0x3f));
++			s[4] = (char)(0x80 | (c & 0x3f));
++		}
++		DBG(("CP 0x%x => %02x %02x %02x %02x %02x", c,
++		    s[0], s[1], s[2], s[3], s[4], s[5]));
++		return 5;
++	} else if (c < 0x80000000) {
++		if (slen >= 6) {
++			s[0] = (char)(0xfc | (c >> 30));
++			s[1] = (char)(0x80 | ((c >> 24) & 0x3f));
++			s[2] = (char)(0x80 | ((c >> 18) & 0x3f));
++			s[3] = (char)(0x80 | ((c >> 12) & 0x3f));
++			s[4] = (char)(0x80 | ((c >> 6) & 0x3f));
++			s[5] = (char)(0x80 | (c & 0x3f));
++		}
++		DBG(("CP 0x%x => %02x %02x %02x %02x %02x %02x", c,
++		    s[0], s[1], s[2], s[3], s[4], s[5], s[6]));
++		return 6;
++#endif
++	}
++	DBG(("INTERNAL ERROR: CP 0x%x not encodeable", c));
++	return -1;
++}
++
++static int
++code_in_table(u_int32_t c, const struct u32_range *table, size_t tlen)
++{
++	const struct u32_range *e, *end = (void *)(tlen + (char *)table);
++
++	for (e = table; e < end; e++) {
++		if (c >= e->lo && c <= e->hi)
++			return 1;
++	}
++	return 0;
++}
++
++
++int
++ssh_utf8_stringprep(const char *in, char *_out, size_t olen)
++{
++	u_char *out = (u_char *)_out;
++	int r, state = 0;
++	size_t i, o, ilen = strlen(in);
++	u_int32_t c, e;
++
++	if (olen < 1)
++		return -1;
++
++	e = c = 0;
++	for (i = o = 0; i < ilen; i++) {
++		e = (u_char)in[i];
++		DBG(("top: i=%lu c=0x%02x e=0x%02x '%c' state=%d",
++		    (u_long)i, c, e, isprint(e) ? e : ' ', state));
++		/* Invalid code point state */
++		if (state == -1) {
++			/*
++			 * Continue eating continuation characters until
++			 * a new start character comes along.
++			 */
++			if ((e & 0xc0) == 0x80)
++				continue;
++			state = 0;
++		}
++
++		/* New code point state */
++		if (state == 0) {
++			DBG(("new code point"));
++			if ((e & 0x80) == 0) {
++				/* 7 bit code point: skip to output */
++				c = e & 0x7f;
++				DBG(("7 bit CP: c=0x%02x '%c'",
++				    c, isprint(c) ? c : '.'));
++				goto have_code;
++			}
++			if ((e & 0xe0) == 0xc0) {
++				/* 11 bit code point */
++				state = 1;
++				c = (e & 0x1f) << 6;
++				DBG(("start 11 bit CP: c=0x%02x", c));
++				if (c == 0)
++					goto bad_encoding;
++				continue;
++			}
++			if ((e & 0xf0) == 0xe0) {
++				/* 16 bit code point */
++				state = 2;
++				c = (e & 0xf) << 12;
++				DBG(("start 16 bit CP: c=0x%02x", c));
++				if (c == 0)
++					goto bad_encoding;
++				continue;
++			}
++			if ((e & 0xf8) == 0xf0) {
++				/* 21 bit code point */
++				state = 3;
++				c = (e & 0x7) << 18;
++				DBG(("start 21 bit CP: c=0x%02x", c));
++				if (c == 0)
++					goto bad_encoding;
++				continue;
++			}
++#if BANNED_RFC3629
++			if ((e & 0xfc) == 0xf8) {
++				/* 26 bit code point */
++				state = 4;
++				c = (e & 0x3) << 24;
++				DBG(("start 26 bit CP: c=0x%02x", c));
++				if (c == 0)
++					goto bad_encoding;
++				continue;
++			}
++			if ((e & 0xfe) == 0xfc) {
++				/* 31 bit code point */
++				state = 5;
++				c = (e & 0x1) << 30;
++				DBG(("start 31 bit CP: c=0x%02x", c));
++				if (c == 0)
++					goto bad_encoding;
++				continue;
++			}
++#endif
++ bad_encoding:
++			DBG(("bad encoding"));
++			c = 0;
++			state = -1;
++			continue;
++		}
++
++		/* In multibyte code point state */
++		if (state < 1 || state > 5) {
++			DBG(("INTERNAL ERROR: invalid state %d", state));
++			return -1;
++		}
++		DBG(("CP continuation; before c=0x%02x state=%d", c, state));
++		state--;
++		c |= (e & 0x3f) << (state * 6);	
++		DBG(("CP continuation; after c=0x%02x state=%d", c, state));
++		if (state > 0)
++			continue;
++
++ have_code:
++		DBG(("CP done; final c=0x%02x", c));
++		/* Character finished. prepare, encode and output */
++		if (c == 0)
++			break; /* shouldn't happen */
++
++		/* RFC3629 bans codepoints > U+10FFFF */
++		if (c > 0x10FFFF) {
++			DBG(("CP 0x%x > 0x10FFFF", c));
++			goto bad_encoding;
++		}
++		/* Mapping */
++		if (code_in_table(c, map_to_nothing, sizeof(map_to_nothing))) {
++			DBG(("CP 0x%x mapped to nothing", c));
++			continue;
++		}
++		/* Prohibitied output */
++		if (code_in_table(c, prohibited, sizeof(prohibited)) &&
++		    !code_in_table(c, whitelist, sizeof(whitelist))) {
++			DBG(("CP 0x%x is in prohibited list", c));
++			return -1;
++		}
++		/* Map unassigned code points to U+FFFD */
++		if (code_in_table(c, unassigned, sizeof(unassigned))) {
++			DBG(("CP 0x%x is unassigned", c));
++			c = 0xFFFD;
++		}
++
++		/* Encode the character */
++		r = encode_utf8(c, out + o, olen - o - 1);
++		DBG(("CP 0x%x encode returned %d o=%lu of %lu",
++		    c, r, (u_long)o, (u_long)olen));
++		if (r < 0)
++			return -1;
++		o += r;
++	}
++	out[o] = '\0';
++	return 0;
++}
++
++#if 0
++#include <err.h>
++
++int
++main(int argc, char **argv)
++{
++	char in[8192], out[8192];
++	
++	while (fgets(in, sizeof(in), stdin) != NULL) {
++		if (ssh_utf8_stringprep(in, out, sizeof(out)) != 0)
++			errx(1, "bad input");
++		fputs(out, stdout);
++	}
++	return 0;
++}
++#endif


More information about the scm-commits mailing list