Linux in-kernel keys support

David Härdeman david at 2gen.com
Wed Jul 27 07:08:11 EST 2005


Hi all,

I recently made a patch to openssh 4.1p1 to allow it to use the 
in-kernel key management provided by 2.6.12 or later Linux kernels.

I've attached the patch (which is still only a proof-of-concept, for 
instance its very verbose right now) to this mail.

Now, my question is, is this a completely insane idea and would (a later 
version of) the patch have a chance of making it into the portable 
version?

Also, in its current incarnation, the patch stores the unencrypted 
secret key in the kernel keyring meaning that its available to the user 
who added it....I'm not sure if that is a problem security-wise...

To compile:
Apply the patch to 4.1p1, run autoconf, make sure that the key-utils 
library from 
http://people.redhat.com/~dhowells/keyutils/keyutils-0.1.tar.bz2 is 
comiled and installed and also copy the keyutil.h header into the 
openssh src dir. Then run configure and compile.

To use:
First run "ssh-add -k" to add the key to the kernel keyring and then use 
ssh as usual...it will use any ssh keys it finds in the kernel keyring.

Feedback and flames welcome.

Regards,
David Härdeman
david at 2gen.com

-------------- next part --------------
diff -ubr -x configure openssh-4.1p1/config.h.in openssh-4.1p1-hacked/config.h.in
--- openssh-4.1p1/config.h.in	2005-05-25 14:26:09.000000000 +0200
+++ openssh-4.1p1-hacked/config.h.in	2005-07-25 23:18:17.000000000 +0200
@@ -694,6 +694,9 @@
 /* Define to 1 if you have the <libgen.h> header file. */
 #undef HAVE_LIBGEN_H
 
+/* Define to 1 if you have the `keyutil' library (-lkeyutil). */
+#undef HAVE_LIBKEYUTIL
+
 /* Define to 1 if you have the `nsl' library (-lnsl). */
 #undef HAVE_LIBNSL
 
diff -ubr -x configure openssh-4.1p1/configure.ac openssh-4.1p1-hacked/configure.ac
--- openssh-4.1p1/configure.ac	2005-04-24 09:52:23.000000000 +0200
+++ openssh-4.1p1-hacked/configure.ac	2005-07-25 23:15:32.000000000 +0200
@@ -261,6 +261,7 @@
 	AC_DEFINE(LINK_OPNOTSUPP_ERRNO, EPERM)
 	AC_DEFINE(_PATH_BTMP, "/var/log/btmp", [log for bad login attempts])
 	AC_DEFINE(USE_BTMP, 1, [Use btmp to log bad logins])
+	AC_CHECK_LIB(keyutil, keyctl_read)
 	inet6_default_4in6=yes
 	case `uname -r` in
 	1.*|2.0.*)
diff -ubr -x configure openssh-4.1p1/key.c openssh-4.1p1-hacked/key.c
--- openssh-4.1p1/key.c	2004-11-05 10:42:29.000000000 +0100
+++ openssh-4.1p1-hacked/key.c	2005-07-25 22:13:45.000000000 +0200
@@ -545,6 +545,9 @@
 key_ssh_name(const Key *k)
 {
 	switch (k->type) {
+	case KEY_RSA1:
+		return "rsa1";
+		break;
 	case KEY_RSA:
 		return "ssh-rsa";
 		break;
@@ -698,6 +701,7 @@
 	type = key_type_from_name(ktype);
 
 	switch (type) {
+	case KEY_RSA1:
 	case KEY_RSA:
 		key = key_new(type);
 		if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 ||
@@ -762,6 +766,7 @@
 		buffer_put_bignum2(&b, key->dsa->g);
 		buffer_put_bignum2(&b, key->dsa->pub_key);
 		break;
+	case KEY_RSA1:
 	case KEY_RSA:
 		buffer_put_cstring(&b, key_ssh_name(key));
 		buffer_put_bignum2(&b, key->rsa->e);
diff -ubr -x configure openssh-4.1p1/key.h openssh-4.1p1-hacked/key.h
--- openssh-4.1p1/key.h	2003-11-17 11:18:23.000000000 +0100
+++ openssh-4.1p1-hacked/key.h	2005-07-23 22:32:17.000000000 +0200
@@ -47,6 +47,7 @@
 
 /* key is stored in external hardware */
 #define KEY_FLAG_EXT		0x0001
+#define KEY_FLAG_KERN		0x0002
 
 struct Key {
 	int	 type;
diff -ubr -x configure openssh-4.1p1/ssh-add.c openssh-4.1p1-hacked/ssh-add.c
--- openssh-4.1p1/ssh-add.c	2005-03-14 13:08:12.000000000 +0100
+++ openssh-4.1p1-hacked/ssh-add.c	2005-07-25 23:22:08.000000000 +0200
@@ -48,6 +48,9 @@
 #include "authfile.h"
 #include "pathnames.h"
 #include "misc.h"
+#ifdef HAVE_LIBKEYUTIL
+#include "keyutil.h"
+#endif
 
 /* argv0 */
 extern char *__progname;
@@ -66,6 +69,9 @@
 /* User has to confirm key use */
 static int confirm = 0;
 
+/* Should we work on the in-kernel keyring */
+static int inkernel = 0;
+
 /* we keep a cache of one passphrases */
 static char *pass = NULL;
 static void
@@ -85,6 +91,26 @@
 	char *comment = NULL;
 	int ret = -1;
 
+#ifdef HAVE_LIBKEYUTIL
+	if (inkernel) {
+		char buf[MAXPATHLEN + 5];
+		
+		sprintf(buf, "ssh:%s", filename);
+		ret = keyctl_search(KEY_SPEC_USER_SESSION_KEYRING, "user", buf, 0);
+		if (ret < 0) {
+			printf("In-kernel key %s not found\n", filename);
+			return -1;
+		}
+
+		if (keyctl_unlink((key_serial_t) ret, KEY_SPEC_USER_SESSION_KEYRING) < 0) {
+			printf("Error when removing in-kernel key %d\n", ret);
+			return -1;
+		}
+		
+		fprintf(stderr, "In-kernel key %d (%s) removed\n", ret, filename);
+		return 0;
+	}
+#endif
 	public = key_load_public(filename, &comment);
 	if (public == NULL) {
 		printf("Bad key file %s\n", filename);
@@ -108,6 +134,65 @@
 {
 	int ret = -1;
 
+#ifdef HAVE_LIBKEYUTIL
+	if (inkernel) {
+		int count, dlen;
+		char *buf;
+		key_serial_t key, *keylist, *pk;
+		
+		count = keyctl_read_alloc(KEY_SPEC_USER_SESSION_KEYRING, (void **) &keylist);
+		if (count < 0) {
+			printf("Error in keyctl_read_alloc\n");
+			return -1;
+		}
+
+		count /= sizeof(key_serial_t);
+		pk = keylist;
+		ret = 0;
+		while (count--) {
+			key = *pk++;
+
+			if (keyctl_describe_alloc(key, &buf) < 0) {
+				printf("In-kernel key %d inaccessible\n", key);
+				ret = -1;
+				continue;
+			}
+
+			/* Is this a user key? */
+			if (strncmp(buf, "user;", strlen("user;")))
+				goto out;
+
+			dlen = -1;
+			sscanf(buf, "%*[^;];%*[^;];%*[^;];%*[^;];%n", &dlen);		
+			if (dlen == -1) {
+				printf("Unparseable description for in-kernel key %d\n", key);
+				ret = -1;
+				goto out;
+			}
+
+			/* Is this a ssh key? */
+			if (strncmp(buf + dlen, "ssh:", strlen("ssh:")))
+				goto out;
+			
+			
+			if (keyctl_unlink((key_serial_t) key, KEY_SPEC_USER_SESSION_KEYRING) < 0) {
+				ret = -1;
+				printf("Error when removing in-kernel key %d\n", key);
+			}
+			
+			out:
+			free(buf);	
+		};
+
+		free(keylist);
+		if (ret == 0)
+			fprintf(stderr, "All in-kernel identities removed.\n");
+		else
+			fprintf(stderr, "Failed to remove all in-kernel identities.\n");
+		return ret;
+	}
+#endif
+
 	if (ssh_remove_all_identities(ac, 1))
 		ret = 0;
 	/* ignore error-code for ssh2 */
@@ -162,6 +247,29 @@
 		}
 	}
 
+#ifdef HAVE_LIBKEYUTIL
+	if (inkernel) {
+		u_char *blob;
+		u_int len;
+		
+		xfree(comment);
+		comment = xmalloc(strlen(filename) + strlen("ssh:") + 1);
+		sprintf(comment, "ssh:%s", filename);
+
+		fprintf(stderr, "Adding key %s\n", key_fingerprint(private, SSH_FP_MD5, SSH_FP_HEX));	
+		if (!key_to_blob(private, &blob, &len))
+			fatal("key_to_blob: %s\n", filename);
+
+		if (add_key("user", comment, blob, len, KEY_SPEC_USER_SESSION_KEYRING) < 0)
+			fatal("Failed to add key: %s\n", filename);
+
+		xfree(comment);
+		key_free(private);
+		
+		ret = 0;
+		return ret;
+	}
+#endif
 	if (ssh_add_identity_constrained(ac, private, comment, lifetime,
 	    confirm)) {
 		fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
@@ -216,6 +324,91 @@
 	int had_identities = 0;
 	int version;
 
+#ifdef HAVE_LIBKEYUTIL
+	if (inkernel) {
+		int count, dlen;
+		char *buf;
+		u_char *buf2;
+		key_serial_t kkey, *keylist, *pk;
+		int ret = 0;
+		
+		count = keyctl_read_alloc(KEY_SPEC_USER_SESSION_KEYRING, (void **) &keylist);
+		if (count < 0) {
+			printf("Error in keyctl_read_alloc\n");
+			return -1;
+		}
+
+		count /= sizeof(key_serial_t);
+		pk = keylist;
+		while (count--) {
+			kkey = *pk++;
+
+			if (keyctl_describe_alloc(kkey, &buf) < 0) {
+				printf("In-kernel key %d inaccessible\n", kkey);
+				ret = -1;
+				continue;
+			}
+
+			/* Is this a user key? */
+			if (strncmp(buf, "user;", strlen("user;")))
+				goto out;
+
+			dlen = -1;
+			sscanf(buf, "%*[^;];%*[^;];%*[^;];%*[^;];%n", &dlen);		
+			if (dlen == -1) {
+				printf("Unparseable description for in-kernel key %d\n", kkey);
+				ret = -1;
+				goto out;
+			}
+
+			/* Is this a ssh key? */
+			if (strncmp(buf + dlen, "ssh:", strlen("ssh:")))
+				goto out;
+			
+			had_identities = 1;
+			comment = buf + dlen;
+			printf("In-kernel key %d (%s)\n", kkey, comment);
+			
+			ret = keyctl_read_alloc(kkey, (void **) &buf2);
+			if (ret < 1) {
+				fprintf(stderr, "Error in keyctl_read_alloc\n");
+				goto out;
+			}
+
+			key = key_from_blob(buf2, ret);
+			free(buf2);
+			if (!key) {
+				fprintf(stderr, "key_from_blob failed: %s\n", comment);
+				goto out;
+			}
+			key->flags = KEY_FLAG_KERN;
+			
+			if (do_fp) {
+				fp = key_fingerprint(key, SSH_FP_MD5,
+				    SSH_FP_HEX);
+				printf("%d %s %s (%s)\n",
+				    key_size(key), fp, comment, key_type(key));
+				xfree(fp);
+			} else {
+				if (!key_write(key, stdout))
+					fprintf(stderr, "key_write failed");
+				fprintf(stdout, " %s\n", comment);
+			}
+			key_free(key);	
+			
+			out:
+			free(buf);	
+		};
+
+		free(keylist);
+		if (!had_identities) {
+			printf("The kernel has no identities.\n");
+			return -1;
+		}
+		return ret;
+	}
+#endif
+
 	for (version = 1; version <= 2; version++) {
 		for (key = ssh_get_first_identity(ac, &comment, version);
 		    key != NULL;
@@ -297,6 +490,9 @@
 	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 HAVE_LIBKEYUTIL
+	fprintf(stderr, "  -k          Work on kernel keyring instead of agent keyring.\n");
+#endif
 #ifdef SMARTCARD
 	fprintf(stderr, "  -s reader   Add key in smartcard reader.\n");
 	fprintf(stderr, "  -e reader   Remove key in smartcard reader.\n");
@@ -307,7 +503,7 @@
 main(int argc, char **argv)
 {
 	extern char *optarg;
-	extern int optind;
+	extern int optind, opterr;
 	AuthenticationConnection *ac = NULL;
 	char *sc_reader_id = NULL;
 	int i, ch, deleting = 0, ret = 0;
@@ -318,13 +514,34 @@
 
 	SSLeay_add_all_algorithms();
 
-	/* At first, get a connection to the authentication agent. */
+#ifdef HAVE_LIBKEYUTIL
+	/* At first, check if we are working on the kernel or agent keys */
+	opterr = 0;
+	while ((ch = getopt(argc, argv, "lLcdDxXe:s:t:k")) != -1) {
+		switch (ch) {
+		case 'k':
+			inkernel = 1;
+			break;
+		default:
+			break;
+		}
+	}
+#endif
+
+	if (!inkernel) {
+		/* Get a connection to the authentication agent. */
 	ac = ssh_get_authentication_connection();
 	if (ac == NULL) {
-		fprintf(stderr, "Could not open a connection to your authentication agent.\n");
+			fprintf(stderr, 
+				"Could not open a connection to your authentication agent.\n");
 		exit(2);
 	}
-	while ((ch = getopt(argc, argv, "lLcdDxXe:s:t:")) != -1) {
+	}
+
+	/* Parse options again */
+	optind = 1;
+	opterr = 1;
+	while ((ch = getopt(argc, argv, "lLcdDxXe:s:t:k")) != -1) {
 		switch (ch) {
 		case 'l':
 		case 'L':
@@ -363,12 +580,25 @@
 				goto done;
 			}
 			break;
+		case 'k':
+			break;
 		default:
 			usage();
 			ret = 1;
 			goto done;
 		}
 	}
+
+	if (inkernel && sc_reader_id != NULL) {
+		fprintf(stderr, "Cannot use smart card reader with in-kernel keys\n");
+		ret = 1;
+		goto done;
+	} else if (inkernel && confirm) {
+		fprintf(stderr, "Cannot use confirmation with in-kernel keys\n");
+		ret = 1;
+		goto done;
+	}
+	
 	argc -= optind;
 	argv += optind;
 	if (sc_reader_id != NULL) {
@@ -410,6 +640,7 @@
 	clear_pass();
 
 done:
+	if (ac != NULL)
 	ssh_close_authentication_connection(ac);
 	return ret;
 }
diff -ubr -x configure openssh-4.1p1/ssh.c openssh-4.1p1-hacked/ssh.c
--- openssh-4.1p1/ssh.c	2005-05-04 07:33:09.000000000 +0200
+++ openssh-4.1p1-hacked/ssh.c	2005-07-25 23:29:10.000000000 +0200
@@ -73,6 +73,10 @@
 #include "monitor_fdpass.h"
 #include "uidswap.h"
 
+#ifdef HAVE_LIBKEYUTIL
+#include "keyutil.h"
+#endif
+
 #ifdef SMARTCARD
 #include "scard.h"
 #endif
@@ -1211,15 +1215,18 @@
 load_public_identity_files(void)
 {
 	char *filename;
-	int i = 0;
+	int i = 0, count;
 	Key *public;
+#ifdef HAVE_LIBKEYUTIL
+	key_serial_t *keylist;
+#endif
 #ifdef SMARTCARD
 	Key **keys;
 
 	if (options.smartcard_device != NULL &&
 	    options.num_identity_files < SSH_MAX_IDENTITY_FILES &&
 	    (keys = sc_get_keys(options.smartcard_device, NULL)) != NULL ) {
-		int count = 0;
+		count = 0;
 		for (i = 0; keys[i] != NULL; i++) {
 			count++;
 			memmove(&options.identity_files[1], &options.identity_files[0],
@@ -1246,6 +1253,95 @@
 		options.identity_files[i] = filename;
 		options.identity_keys[i] = public;
 	}
+
+#ifdef HAVE_LIBKEYUTIL
+	count = keyctl_read_alloc(KEY_SPEC_USER_SESSION_KEYRING, (void **)&keylist);
+	if (count < 0) {
+		fprintf(stderr, "Error in keyctl_read_alloc\n");
+		count = 0;
+	} else {
+		key_serial_t key, *pk;
+		char *buf;
+		int dlen, ret;
+
+		count /= sizeof(key_serial_t);
+		fprintf(stderr, "Count is %d\n", count);
+		if (count == 0)
+			fprintf(stderr, "Keychain empty\n");
+
+		pk = keylist;
+		for (; count > 0; count--) {
+			key = *pk++;
+			
+			ret = keyctl_describe_alloc(key, &buf);
+			if (ret < 0) {
+				printf("%9d: key inaccessible (%m)\n", key);
+				continue;
+			}
+
+			if (strncmp(buf, "user;", strlen("user;"))) {
+				fprintf(stderr, "%d: Not a user key\n", key);
+				goto out;
+			}
+
+			dlen = -1;	
+			sscanf(buf, "%*[^;];%*[^;];%*[^;];%*[^;];%n", &dlen);
+			if (dlen == -1) {
+				fprintf(stderr, "Unparseable description obtained for key %d\n", key);
+				goto out;
+			}
+		
+			fprintf(stderr, "Key %d with desc %s\n", key, buf + dlen);
+
+			if (strncmp(buf + dlen, "ssh:", strlen("ssh:"))) {
+				fprintf(stderr, "%d: Not a ssh key\n", key);
+				goto out;
+			}
+	
+			if (i < SSH_MAX_IDENTITY_FILES) {
+				char kname[1024];
+				
+				fprintf(stderr, "Adding key %d with desc %s\n", key, buf + dlen);
+				
+				/* Get the key payload */
+				free(buf);
+				ret = keyctl_read_alloc(key, (void **) &buf);
+				if (ret < 1) {
+					fprintf(stderr, "Error in keyctl_read_alloc\n");
+					continue;
+				}
+				public = key_from_blob((u_char *)buf, (u_int)ret);
+				free(buf);
+				if (!public)
+					continue;
+				public->flags = KEY_FLAG_KERN;
+fprintf(stderr, "Adding key %s\n", key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX));
+
+				/* Prepare the key description */
+				if (snprintf(kname, 1024, "%s:kernel key %d", key_ssh_name(public), key) < 1) {
+					fprintf(stderr, "Error in snprintf\n");
+					key_free(public);
+					continue;
+				}
+
+				/* Add the key */
+				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.identity_keys[0] = public;
+				options.identity_files[0] = xstrdup(kname);
+				options.num_identity_files++;
+				if (options.num_identity_files > SSH_MAX_IDENTITY_FILES)
+					options.num_identity_files = SSH_MAX_IDENTITY_FILES;
+			}
+			
+			continue;
+			out:
+			free(buf);
+		}
+	}
+#endif /* LIBKEYUTIL */
 }
 
 static void
diff -ubr -x configure openssh-4.1p1/sshconnect1.c openssh-4.1p1-hacked/sshconnect1.c
--- openssh-4.1p1/sshconnect1.c	2004-08-12 14:40:25.000000000 +0200
+++ openssh-4.1p1-hacked/sshconnect1.c	2005-07-25 23:37:13.000000000 +0200
@@ -240,7 +240,7 @@
 	 * load the private key.  Try first with empty passphrase; if it
 	 * fails, ask for a passphrase.
 	 */
-	if (public->flags & KEY_FLAG_EXT)
+	if (public->flags & (KEY_FLAG_EXT | KEY_FLAG_KERN))
 		private = public;
 	else
 		private = key_load_private_type(KEY_RSA1, authfile, "", NULL);
diff -ubr -x configure openssh-4.1p1/sshconnect2.c openssh-4.1p1-hacked/sshconnect2.c
--- openssh-4.1p1/sshconnect2.c	2004-06-15 02:30:09.000000000 +0200
+++ openssh-4.1p1-hacked/sshconnect2.c	2005-07-23 22:39:24.000000000 +0200
@@ -832,7 +832,9 @@
 	 * we have already loaded the private key or
 	 * the private key is stored in external hardware
 	 */
-	if (id->isprivate || (id->key->flags & KEY_FLAG_EXT))
+	if(id->key->flags & KEY_FLAG_KERN)
+		fprintf(stderr, "Going to use kernel key\n");
+	if (id->isprivate || (id->key->flags & KEY_FLAG_EXT) || (id->key->flags & KEY_FLAG_KERN))
 		return (key_sign(id->key, sigp, lenp, data, datalen));
 	/* load the private key from the file */
 	if ((prv = load_identity_file(id->filename)) == NULL)


More information about the openssh-unix-dev mailing list