HostKey in hardware?

andrew cooke andrew at acooke.org
Wed Dec 12 23:24:33 EST 2012


Hi,

I have a client interested in using this patch and would appreciate help +
advice on the best way to move forwards.

My impression is that you're still thinking about the details (PIN etc) and so
this is not in trunk?  And even if it were in trunk, it wuld be for the
FreeBSD reopo, which would then need porting (client on CentOS)?  I imagine
that's too far away to guesstimate a timeline?

So I guess that the only thing I can offer my client now is to move the patch
you provided across to CentOS myself and give them a custom patch / openssh
source.

Apart from the maintenance issues that would imply (which my client can worry
about), what is your opinion of this approach (my client is already reading
this thread)?  The change seemed fairly simple to me, but I am no expert.

Also, if I do any of the above, is there anything that could be fed back to
you to help with your development?  I guess not as I am effectively
snapshotting a random idea...

Sorry for all the questions and thanks for any guidance,
Andrew


On Fri, Nov 23, 2012 at 12:18:14PM +1100, Damien Miller wrote:
> On Wed, 21 Nov 2012, andrew cooke wrote:
> 
> > 
> > Hi,
> > 
> > Is there any way to store HostKey in hardware (and delegate the related
> > processing)?
> 
> I've been wanting to do this for a while, but hadn't got around to it.
> 
> Congratulations, you managed to troll me into action :) (see below)
> 
> > The hardware I am using (Spyrus Lynks II) doesn't have PKCS#11 support, so I
> > would prefer the OpenSSL route (since I already have an engine), but if
> > necessary I would consider writing a minimal PKCS#11 implementation (can
> > anyone give a rough idea of the amount of work involved to get HostKey
> > working, only?)
> 
> We aren't likely to add support for anything other than PKCS#11 host keys.
> 
> Here's a (lightly tested) patch for PKCS#11 host keys. At the moment, the
> keys are loaded using a fixed PIN of 0000, but there's probably a better
> way to do it. I don't really want sshd to block at startup time while looking
> for a password, but my PKCS#15-fu isn't good enough to know how to create
> keys that don't require a PIN at all.
> 
> diff --git servconf.c servconf.c
> index 9919778..3670a2f 100644
> --- servconf.c
> +++ servconf.c
> @@ -67,6 +67,7 @@ initialize_server_options(ServerOptions *options)
>  	options->listen_addrs = NULL;
>  	options->address_family = -1;
>  	options->num_host_key_files = 0;
> +	options->num_host_key_pkcs11_providers = 0;
>  	options->num_host_cert_files = 0;
>  	options->pid_file = NULL;
>  	options->server_key_bits = -1;
> @@ -160,6 +161,7 @@ fill_default_server_options(ServerOptions *options)
>  			    _PATH_HOST_ECDSA_KEY_FILE;
>  		}
>  	}
> +	/* No PKCS#11 providers by default */
>  	/* No certificates by default */
>  	if (options->num_ports == 0)
>  		options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
> @@ -281,7 +283,8 @@ fill_default_server_options(ServerOptions *options)
>  /* Keyword tokens. */
>  typedef enum {
>  	sBadOption,		/* == unknown option */
> -	sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime,
> +	sPort, sHostKeyFile, sHostKeyPKCS11, sServerKeyBits,
> +	sLoginGraceTime, sKeyRegenerationTime,
>  	sPermitRootLogin, sLogFacility, sLogLevel,
>  	sRhostsRSAAuthentication, sRSAAuthentication,
>  	sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
> @@ -324,6 +327,7 @@ static struct {
>  	{ "port", sPort, SSHCFG_GLOBAL },
>  	{ "hostkey", sHostKeyFile, SSHCFG_GLOBAL },
>  	{ "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL },		/* alias */
> +	{ "hostkeypkcs11", sHostKeyPKCS11, SSHCFG_GLOBAL },
>  	{ "pidfile", sPidFile, SSHCFG_GLOBAL },
>  	{ "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL },
>  	{ "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL },
> @@ -907,22 +911,33 @@ process_server_config_line(ServerOptions *options, char *line,
>  			fatal("%s line %d: missing file name.",
>  			    filename, linenum);
>  		if (*activep && *charptr == NULL) {
> -			*charptr = derelativise_path(arg);
> +			if (strcasecmp(arg, "none") == 0)
> +				*charptr = xstrdup("none");
> +			else
> +				*charptr = derelativise_path(arg);
>  			/* increase optional counter */
>  			if (intptr != NULL)
>  				*intptr = *intptr + 1;
>  		}
>  		break;
>  
> +	case sHostKeyPKCS11:
> +		intptr = &options->num_host_key_pkcs11_providers;
> +		if (*intptr >= MAX_HOSTPKCS11)
> +			fatal("%s line %d: too many host key PKCS#11 providers "
> +			    "specified (max %d).", filename, linenum,
> +			    MAX_HOSTPKCS11);
> +		charptr = &options->host_key_pkcs11_providers[*intptr];
> +		goto parse_filename;
> +
>  	case sHostCertificate:
>  		intptr = &options->num_host_cert_files;
> -		if (*intptr >= MAX_HOSTKEYS)
> +		if (*intptr >= MAX_HOSTCERTS)
>  			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;
> @@ -1918,7 +1933,9 @@ dump_config(ServerOptions *o)
>  	    o->authorized_keys_files);
>  	dump_cfg_strarray(sHostKeyFile, o->num_host_key_files,
>  	     o->host_key_files);
> -	dump_cfg_strarray(sHostKeyFile, o->num_host_cert_files,
> +	dump_cfg_strarray(sHostKeyPKCS11, o->num_host_key_pkcs11_providers,
> +	    o->host_key_pkcs11_providers);
> +	dump_cfg_strarray(sHostCertificate, 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);
> diff --git servconf.h servconf.h
> index da2374b..e48b38a 100644
> --- servconf.h
> +++ servconf.h
> @@ -23,8 +23,9 @@
>  #define MAX_ALLOW_GROUPS	256	/* Max # groups on allow list. */
>  #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_HOSTKEYS		16	/* Max # hostkeys. */
> +#define MAX_HOSTCERTS		16	/* Max # host certificates. */
> +#define MAX_HOSTPKCS11		16	/* Max # host key PKCS#11 providers. */
>  #define MAX_ACCEPT_ENV		256	/* Max # of env vars. */
>  #define MAX_MATCH_GROUPS	256	/* Max # of groups for Match. */
>  #define MAX_AUTHKEYS_FILES	256	/* Max # of authorized_keys files. */
> @@ -55,10 +56,17 @@ typedef struct {
>  	char   *listen_addr;		/* Address on which the server listens. */
>  	struct addrinfo *listen_addrs;	/* Addresses on which the server listens. */
>  	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. */
> +
> +	/* Host key files */
> +	char   *host_key_files[MAX_HOSTKEYS];
> +	int     num_host_key_files;
> +	/* Host key PKCS#11 providers */
> +	char   *host_key_pkcs11_providers[MAX_HOSTPKCS11];
> +	int     num_host_key_pkcs11_providers;
> +	/* Host certificate files */
> +	char   *host_cert_files[MAX_HOSTCERTS];
> +	int     num_host_cert_files;
> +
>  	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
> diff --git sshd.c sshd.c
> index d3f53c0..83eab58 100644
> --- sshd.c
> +++ sshd.c
> @@ -105,6 +105,13 @@
>  #include "ssh-sandbox.h"
>  #include "version.h"
>  
> +#ifdef ENABLE_PKCS11
> +#include "ssh-pkcs11.h"
> +#endif
> +
> +/* PIN used for PKCS#11 providers */
> +#define SSHD_PKCS11_PIN	"0000"	/* XXX */
> +
>  #ifdef LIBWRAP
>  #include <tcpd.h>
>  #include <syslog.h>
> @@ -188,6 +195,7 @@ Kex *xxx_kex;
>  struct {
>  	Key	*server_key;		/* ephemeral server key */
>  	Key	*ssh1_host_key;		/* ssh1 host key */
> +	int	num_host_keys;		/* number of private host keys */
>  	Key	**host_keys;		/* all private host keys */
>  	Key	**host_certificates;	/* all public host certificates */
>  	int	have_ssh1_key;
> @@ -534,7 +542,7 @@ destroy_sensitive_data(void)
>  		key_free(sensitive_data.server_key);
>  		sensitive_data.server_key = NULL;
>  	}
> -	for (i = 0; i < options.num_host_key_files; i++) {
> +	for (i = 0; i < sensitive_data.num_host_keys; i++) {
>  		if (sensitive_data.host_keys[i]) {
>  			key_free(sensitive_data.host_keys[i]);
>  			sensitive_data.host_keys[i] = NULL;
> @@ -561,7 +569,7 @@ demote_sensitive_data(void)
>  		sensitive_data.server_key = tmp;
>  	}
>  
> -	for (i = 0; i < options.num_host_key_files; i++) {
> +	for (i = 0; i < sensitive_data.num_host_keys; i++) {
>  		if (sensitive_data.host_keys[i]) {
>  			tmp = key_demote(sensitive_data.host_keys[i]);
>  			key_free(sensitive_data.host_keys[i]);
> @@ -747,7 +755,7 @@ list_hostkey_types(void)
>  	Key *key;
>  
>  	buffer_init(&b);
> -	for (i = 0; i < options.num_host_key_files; i++) {
> +	for (i = 0; i < sensitive_data.num_host_keys; i++) {
>  		key = sensitive_data.host_keys[i];
>  		if (key == NULL)
>  			continue;
> @@ -791,7 +799,7 @@ get_hostkey_by_type(int type, int need_private)
>  	int i;
>  	Key *key;
>  
> -	for (i = 0; i < options.num_host_key_files; i++) {
> +	for (i = 0; i < sensitive_data.num_host_keys; i++) {
>  		switch (type) {
>  		case KEY_RSA_CERT_V00:
>  		case KEY_DSA_CERT_V00:
> @@ -826,7 +834,7 @@ get_hostkey_private_by_type(int type)
>  Key *
>  get_hostkey_by_index(int ind)
>  {
> -	if (ind < 0 || ind >= options.num_host_key_files)
> +	if (ind < 0 || ind >= sensitive_data.num_host_keys)
>  		return (NULL);
>  	return (sensitive_data.host_keys[ind]);
>  }
> @@ -836,7 +844,7 @@ get_hostkey_index(Key *key)
>  {
>  	int i;
>  
> -	for (i = 0; i < options.num_host_key_files; i++) {
> +	for (i = 0; i < sensitive_data.num_host_keys; i++) {
>  		if (key_is_cert(key)) {
>  			if (key == sensitive_data.host_certificates[i])
>  				return (i);
> @@ -1296,7 +1304,7 @@ main(int ac, char **av)
>  {
>  	extern char *optarg;
>  	extern int optind;
> -	int opt, i, j, on = 1;
> +	int opt, i, j, nkeys, on = 1;
>  	int sock_in = -1, sock_out = -1, newsock = -1;
>  	const char *remote_ip;
>  	int remote_port;
> @@ -1305,7 +1313,7 @@ main(int ac, char **av)
>  	u_int n;
>  	u_int64_t ibytes, obytes;
>  	mode_t new_umask;
> -	Key *key;
> +	Key *key, **keys;
>  	Authctxt *authctxt;
>  	struct connection_info *connection_info = get_connection_info(0, 0);
>  
> @@ -1530,20 +1538,54 @@ main(int ac, char **av)
>  	debug("sshd version %.100s", SSH_VERSION);
>  
>  	/* load private host keys */
> -	sensitive_data.host_keys = xcalloc(options.num_host_key_files,
> -	    sizeof(Key *));
> -	for (i = 0; i < options.num_host_key_files; i++)
> -		sensitive_data.host_keys[i] = NULL;
> +	sensitive_data.host_keys = xcalloc(options.num_host_key_files + 
> +	    options.num_host_key_pkcs11_providers, sizeof(Key *));
> +	sensitive_data.num_host_keys = 0;
> +
> +#ifdef ENABLE_PKCS11
> +	if (options.num_host_key_pkcs11_providers > 0) {
> +		if (pkcs11_init(0) != 0)
> +			fatal("Could not initialise PKCS#11 for host keys");
> +		for (i = 0; i < options.num_host_key_pkcs11_providers; i++) {
> +			nkeys = pkcs11_add_provider(
> +			    options.host_key_pkcs11_providers[i],
> +			    SSHD_PKCS11_PIN, &keys);
> +			if (nkeys == -1)
> +				fatal("Failed to add PKCS#11 provider \"%s\"",
> +				    options.host_key_pkcs11_providers[i]);
> +			if (nkeys == 0) {
> +				error("PKCS#11 provider \"%s\" yielded no keys",
> +				    options.host_key_pkcs11_providers[i]);
> +				continue;
> +			}
> +			for (j = 0; j < nkeys; j++) {
> +				sensitive_data.host_keys[
> +				    sensitive_data.num_host_keys++] = keys[j];
> +			}
> +			free(keys);
> +		}
> +	}
> +#endif /* ENABLE_PKCS11 */
>  
>  	for (i = 0; i < options.num_host_key_files; i++) {
> +		if (strcasecmp(options.host_key_files[i], "none") == 0)
> +			continue;
>  		key = key_load_private(options.host_key_files[i], "", NULL);
> -		sensitive_data.host_keys[i] = key;
> +		sensitive_data.host_keys[sensitive_data.num_host_keys++] = key;
>  		if (key == NULL) {
>  			error("Could not load host key: %s",
>  			    options.host_key_files[i]);
> -			sensitive_data.host_keys[i] = NULL;
>  			continue;
>  		}
> +		debug("private host key: #%d type %d %s",
> +		    sensitive_data.num_host_keys - 1, key->type, key_type(key));
> +	}
> +
> +	/* Figure out whether we have loaded keys for protocols 1 and 2 */
> +	for (i = 0; i < sensitive_data.num_host_keys; i++) {
> +		key = sensitive_data.host_keys[i];
> +		if (key == NULL)
> +			continue;
>  		switch (key->type) {
>  		case KEY_RSA1:
>  			sensitive_data.ssh1_host_key = key;
> @@ -1555,9 +1597,8 @@ main(int ac, char **av)
>  			sensitive_data.have_ssh2_key = 1;
>  			break;
>  		}
> -		debug("private host key: #%d type %d %s", i, key->type,
> -		    key_type(key));
>  	}
> +
>  	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;
> @@ -1575,9 +1616,9 @@ main(int ac, char **av)
>  	 * 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,
> +	sensitive_data.host_certificates = xcalloc(sensitive_data.num_host_keys,
>  	    sizeof(Key *));
> -	for (i = 0; i < options.num_host_key_files; i++)
> +	for (i = 0; i < sensitive_data.num_host_keys; i++)
>  		sensitive_data.host_certificates[i] = NULL;
>  
>  	for (i = 0; i < options.num_host_cert_files; i++) {
> @@ -1594,14 +1635,14 @@ main(int ac, char **av)
>  			continue;
>  		}
>  		/* Find matching private key */
> -		for (j = 0; j < options.num_host_key_files; j++) {
> +		for (j = 0; j < sensitive_data.num_host_keys; 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) {
> +		if (j >= sensitive_data.num_host_keys) {
>  			error("No matching private key for certificate: %s",
>  			    options.host_cert_files[i]);
>  			key_free(key);
> diff --git sshd_config.5 sshd_config.5
> index 91935d0..5ec06f2 100644
> --- sshd_config.5
> +++ sshd_config.5
> @@ -517,6 +517,10 @@ for protocol version 1, and
>  and
>  .Pa /etc/ssh/ssh_host_rsa_key
>  for protocol version 2.
> +It is possible to skip loading of host keys by specifying a path of
> +.Pa none
> +in cases where keys are supplied via
> +.Cm HostKeyPKCS11 .
>  Note that
>  .Xr sshd 8
>  will refuse to use a file if it is group/world-accessible.
> @@ -528,6 +532,15 @@ keys are used for version 1 and
>  or
>  .Dq rsa
>  are used for version 2 of the SSH protocol.
> +.It Cm HostKeyPKCS11
> +Specify a PKCS#11 provider for host keys.
> +.Nm
> +will attempt to load all keys in this device assuming a PIN of
> +.Dq 0000
> +and use them as host keys.
> +This option may be specified more than once to allow loading of host keys
> +from multiple devices.
> +The default is not to attempt to load host keys from PKCS#11 devices.
>  .It Cm IgnoreRhosts
>  Specifies that
>  .Pa .rhosts
> 


More information about the openssh-unix-dev mailing list