[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