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