aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2015-01-20 17:25:20 +0100
committerAndreas Schneider <asn@cryptomilk.org>2015-02-02 14:45:52 +0100
commit3ec3a926e5da5131917c7f4e66a1a865c2b524f1 (patch)
treea4190e494d9079439e0dfc9c6f5c057104cc9e60
parent2f7886837f44351fc00e1a251c464bf6de2da2d1 (diff)
downloadlibssh-3ec3a926e5da5131917c7f4e66a1a865c2b524f1.tar.gz
libssh-3ec3a926e5da5131917c7f4e66a1a865c2b524f1.tar.xz
libssh-3ec3a926e5da5131917c7f4e66a1a865c2b524f1.zip
ed25519: Add support o import OpenSSH container keys
Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
-rw-r--r--include/libssh/pki_priv.h8
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/pki.c33
-rw-r--r--src/pki_container_openssh.c254
4 files changed, 286 insertions, 10 deletions
diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h
index 5751d412..50b58950 100644
--- a/include/libssh/pki_priv.h
+++ b/include/libssh/pki_priv.h
@@ -27,6 +27,10 @@
#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----"
#define ECDSA_HEADER_BEGIN "-----BEGIN EC PRIVATE KEY-----"
#define ECDSA_HEADER_END "-----END EC PRIVATE KEY-----"
+#define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
+#define OPENSSH_HEADER_END "-----END OPENSSH PRIVATE KEY-----"
+/* Magic defined in OpenSSH/PROTOCOL.key */
+#define OPENSSH_AUTH_MAGIC "openssh-key-v1"
#define ssh_pki_log(...) \
_ssh_pki_log(__FUNCTION__, __VA_ARGS__)
@@ -105,4 +109,8 @@ int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key);
ssh_string pki_ed25519_sig_to_blob(ssh_signature sig);
int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob);
+/* PKI Container OpenSSH */
+ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
+ const char *passphrase, ssh_auth_callback auth_fn, void *auth_data);
+
#endif /* PKI_PRIV_H_ */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a4bc8595..ade4903f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -139,6 +139,7 @@ set(libssh_SRCS
packet_crypt.c
pcap.c
pki.c
+ pki_container_openssh.c
pki_ed25519.c
poll.c
session.c
diff --git a/src/pki.c b/src/pki.c
index 3b859bb2..b35fedd8 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -1,5 +1,5 @@
/*
- * known_hosts.c
+ * pki.c
* This file is part of the SSH Library
*
* Copyright (c) 2010 by Aris Adamantiadis
@@ -402,6 +402,7 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
ssh_key *pkey)
{
ssh_key key;
+ int cmp;
if (b64_key == NULL || pkey == NULL) {
return SSH_ERROR;
@@ -414,7 +415,20 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
ssh_pki_log("Trying to decode privkey passphrase=%s",
passphrase ? "true" : "false");
- key = pki_private_key_from_base64(b64_key, passphrase, auth_fn, auth_data);
+ /* Test for OpenSSH key format first */
+ cmp = strncmp(b64_key, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
+ if (cmp == 0) {
+ key = ssh_pki_openssh_privkey_import(b64_key,
+ passphrase,
+ auth_fn,
+ auth_data);
+ } else {
+ /* fallback on PEM decoder */
+ key = pki_private_key_from_base64(b64_key,
+ passphrase,
+ auth_fn,
+ auth_data);
+ }
if (key == NULL) {
return SSH_ERROR;
}
@@ -451,7 +465,6 @@ int ssh_pki_import_privkey_file(const char *filename,
ssh_key *pkey) {
struct stat sb;
char *key_buf;
- ssh_key key;
FILE *file;
off_t size;
int rc;
@@ -505,14 +518,14 @@ int ssh_pki_import_privkey_file(const char *filename,
}
key_buf[size] = 0;
- key = pki_private_key_from_base64(key_buf, passphrase, auth_fn, auth_data);
- SAFE_FREE(key_buf);
- if (key == NULL) {
- return SSH_ERROR;
- }
+ rc = ssh_pki_import_privkey_base64(key_buf,
+ passphrase,
+ auth_fn,
+ auth_data,
+ pkey);
- *pkey = key;
- return SSH_OK;
+ SAFE_FREE(key_buf);
+ return rc;
}
/**
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c
new file mode 100644
index 00000000..beb3789a
--- /dev/null
+++ b/src/pki_container_openssh.c
@@ -0,0 +1,254 @@
+/*
+ * pki_container_openssh.c
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2013,2014 Aris Adamantiadis <aris@badcode.be>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/**
+ * @ingroup libssh_pki
+ * *
+ * @{
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <string.h>
+
+#include "libssh/libssh.h"
+#include "libssh/priv.h"
+#include "libssh/pki.h"
+#include "libssh/pki_priv.h"
+#include "libssh/buffer.h"
+
+
+/**
+ * @internal
+ *
+ * @brief Import a private key from a ssh buffer.
+ *
+ * @param[in] key_blob_buffer The key blob to import as specified in
+ * key.c:key_private_serialize in OpenSSH source
+ * code.
+ *
+ * @param[out] pkey A pointer where the allocated key can be stored. You
+ * need to free the memory.
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ *
+ * @see ssh_key_free()
+ */
+static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
+ ssh_key *pkey)
+{
+ enum ssh_keytypes_e type;
+ char *type_s = NULL;
+ ssh_key key = NULL;
+ ssh_string pubkey = NULL, privkey = NULL;
+ int rc;
+
+ if (pkey == NULL) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_buffer_unpack(key_blob_buffer, "s", &type_s);
+ if (rc == SSH_ERROR){
+ ssh_pki_log("Unpack error");
+ return SSH_ERROR;
+ }
+
+ type = ssh_key_type_from_name(type_s);
+ if (type == SSH_KEYTYPE_UNKNOWN) {
+ ssh_pki_log("Unknown key type found!");
+ return SSH_ERROR;
+ }
+ SAFE_FREE(type_s);
+
+ key = ssh_key_new();
+ if (key == NULL) {
+ ssh_pki_log("Out of memory");
+ return SSH_ERROR;
+ }
+
+ key->type = type;
+ key->type_c = ssh_key_type_to_char(type);
+ key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
+
+ switch (type) {
+ case SSH_KEYTYPE_ED25519:
+ rc = ssh_buffer_unpack(key_blob_buffer, "SS", &pubkey, &privkey);
+ if (rc != SSH_OK){
+ ssh_pki_log("Unpack error");
+ goto fail;
+ }
+ if(ssh_string_len(pubkey) != ED25519_PK_LEN ||
+ ssh_string_len(privkey) != ED25519_SK_LEN){
+ ssh_pki_log("Invalid ed25519 key len");
+ goto fail;
+ }
+ key->ed25519_privkey = malloc(ED25519_SK_LEN);
+ key->ed25519_pubkey = malloc(ED25519_PK_LEN);
+ if(key->ed25519_privkey == NULL || key->ed25519_pubkey == NULL){
+ goto fail;
+ }
+ memcpy(key->ed25519_privkey, ssh_string_data(privkey), ED25519_SK_LEN);
+ memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_PK_LEN);
+ memset(ssh_string_data(privkey), 0, ED25519_SK_LEN);
+ SAFE_FREE(privkey);
+ SAFE_FREE(pubkey);
+ break;
+ case SSH_KEYTYPE_DSS:
+ /* p,q,g,pub_key,priv_key */
+ case SSH_KEYTYPE_RSA:
+ /* n,e,d,iqmp,p,q */
+ case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_ECDSA:
+ /* curve_name, group, privkey */
+ ssh_pki_log("Unsupported private key method %s", key->type_c);
+ goto fail;
+ case SSH_KEYTYPE_UNKNOWN:
+ ssh_pki_log("Unknown private key protocol %s", key->type_c);
+ goto fail;
+ }
+
+ *pkey = key;
+ return SSH_OK;
+fail:
+ ssh_key_free(key);
+ if(privkey != NULL){
+ memset(ssh_string_data(privkey), 0, ssh_string_len(privkey));
+ }
+ SAFE_FREE(pubkey);
+ SAFE_FREE(privkey);
+
+ return SSH_ERROR;
+}
+
+/** @internal
+ * @brief Import a private key in OpenSSH (new) format. This format is
+ * typically used with ed25519 keys but can be used for others.
+ */
+ ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data)
+{
+ const char *ptr=text_key;
+ const char *end;
+ char *base64;
+ int cmp;
+ int rc;
+ int i;
+ ssh_buffer buffer = NULL, privkey_buffer=NULL;
+ char *magic = NULL, *ciphername = NULL, *kdfname = NULL;
+ uint32_t nkeys = 0, checkint1, checkint2;
+ ssh_string kdfoptions = NULL;
+ ssh_string pubkey0 = NULL;
+ ssh_string privkeys = NULL;
+ ssh_key key = NULL;
+
+ cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
+ if (cmp != 0){
+ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no header)");
+ goto error;
+ }
+ ptr += strlen(OPENSSH_HEADER_BEGIN);
+ while(ptr[0] != '\0' && !isspace((int)ptr[0])) {
+ ptr++;
+ }
+ end = strstr(ptr, OPENSSH_HEADER_END);
+ if (end == NULL){
+ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)");
+ goto error;
+ }
+ base64 = malloc(end - ptr + 1);
+ if (base64 == NULL){
+ goto error;
+ }
+ for (i = 0; ptr < end; ptr++){
+ if (!isspace((int)ptr[0])) {
+ base64[i] = ptr[0];
+ i++;
+ }
+ }
+ base64[i] = '\0';
+ buffer = base64_to_bin(base64);
+ SAFE_FREE(base64);
+ if (buffer == NULL){
+ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)");
+ goto error;
+ }
+ rc = ssh_buffer_unpack(buffer, "PssSdSS",
+ strlen(OPENSSH_AUTH_MAGIC) + 1,
+ &magic,
+ &ciphername,
+ &kdfname,
+ &kdfoptions,
+ &nkeys,
+ &pubkey0,
+ &privkeys);
+ if (rc == SSH_ERROR){
+ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)");
+ goto error;
+ }
+ cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC));
+ if (cmp != 0){
+ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)");
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_INFO, "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d\n", ciphername, kdfname, nkeys);
+ if (strcmp(ciphername, "none") != 0){
+ SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
+ goto error;
+ }
+ if (nkeys != 1){
+ SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys);
+ goto error;
+ }
+
+ privkey_buffer = ssh_buffer_new();
+ ssh_buffer_add_data(privkey_buffer, ssh_string_data(privkeys), ssh_string_len(privkeys));
+ rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2);
+ if (rc == SSH_ERROR || checkint1 != checkint2){
+ SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)");
+ goto error;
+ }
+ rc = pki_openssh_import_privkey_blob(privkey_buffer, &key);
+
+error:
+ if(buffer != NULL){
+ ssh_buffer_free(buffer);
+ buffer = NULL;
+ }
+ if(privkey_buffer != NULL){
+ ssh_buffer_free(privkey_buffer);
+ privkey_buffer = NULL;
+ }
+ SAFE_FREE(magic);
+ SAFE_FREE(ciphername);
+ SAFE_FREE(kdfname);
+ SAFE_FREE(kdfoptions);
+ SAFE_FREE(pubkey0);
+ SAFE_FREE(privkeys);
+ return key;
+}
+
+/**
+ * @}
+ */