diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h index f8d94616..30af8d86 100644 --- a/include/libssh/pki_priv.h +++ b/include/libssh/pki_priv.h @@ -149,6 +149,7 @@ int pki_privkey_build_ed25519(ssh_key key, ssh_string privkey); /* PKI Container OpenSSH */ +ssh_key ssh_pki_openssh_pubkey_import(const char *text_key); ssh_key ssh_pki_openssh_privkey_import(const char *text_key, const char *passphrase, ssh_auth_callback auth_fn, void *auth_data); ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, diff --git a/src/pki.c b/src/pki.c index f819b94d..22ff3cd5 100644 --- a/src/pki.c +++ b/src/pki.c @@ -1320,7 +1320,7 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey) const char *q; FILE *file; off_t size; - int rc; + int rc, cmp; if (pkey == NULL || filename == NULL || *filename == '\0') { return SSH_ERROR; @@ -1370,6 +1370,20 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey) key_buf[size] = '\0'; buflen = strlen(key_buf); + /* Test for new OpenSSH key format first */ + cmp = strncmp(key_buf, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN)); + if (cmp == 0) { + *pkey = ssh_pki_openssh_pubkey_import(key_buf); + SAFE_FREE(key_buf); + if (*pkey == NULL) { + SSH_LOG(SSH_LOG_WARN, "Failed to import public key from OpenSSH" + " private key file"); + return SSH_ERROR; + } + return SSH_OK; + } + + /* This the old one-line public key format */ q = p = key_buf; for (i = 0; i < buflen; i++) { if (isspace((int)p[i])) { diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c index 4154b424..a287d71d 100644 --- a/src/pki_container_openssh.c +++ b/src/pki_container_openssh.c @@ -30,6 +30,7 @@ #include #include +#include #include "libssh/libssh.h" #include "libssh/priv.h" @@ -225,10 +226,12 @@ static int pki_private_key_decrypt(ssh_string blob, * @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) +static ssh_key +ssh_pki_openssh_import(const char *text_key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + bool private) { const char *ptr=text_key; const char *end; @@ -249,7 +252,7 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, 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; + goto out; } ptr += strlen(OPENSSH_HEADER_BEGIN); while(ptr[0] != '\0' && !isspace((int)ptr[0])) { @@ -258,11 +261,11 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, end = strstr(ptr, OPENSSH_HEADER_END); if (end == NULL){ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)"); - goto error; + goto out; } base64 = malloc(end - ptr + 1); if (base64 == NULL){ - goto error; + goto out; } for (i = 0; ptr < end; ptr++){ if (!isspace((int)ptr[0])) { @@ -275,7 +278,7 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, SAFE_FREE(base64); if (buffer == NULL){ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)"); - goto error; + goto out; } rc = ssh_buffer_unpack(buffer, "PssSdSS", strlen(OPENSSH_AUTH_MAGIC) + 1, @@ -288,12 +291,12 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, &privkeys); if (rc == SSH_ERROR){ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)"); - goto error; + goto out; } 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; + goto out; } SSH_LOG(SSH_LOG_INFO, "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d\n", @@ -302,8 +305,18 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, nkeys); if (nkeys != 1){ SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys); - goto error; + goto out; } + + /* If we are interested only in public key do not progress + * to the key decryption later + */ + if (!private) { + rc = ssh_pki_import_pubkey_blob(pubkey0, &key); + /* in either case we clean up here */ + goto out; + } + rc = pki_private_key_decrypt(privkeys, passphrase, ciphername, @@ -312,13 +325,13 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, auth_fn, auth_data); if (rc == SSH_ERROR){ - goto error; + goto out; } privkey_buffer = ssh_buffer_new(); if (privkey_buffer == NULL) { rc = SSH_ERROR; - goto error; + goto out; } ssh_buffer_set_secure(privkey_buffer); @@ -329,11 +342,11 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, 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; + goto out; } rc = pki_openssh_import_privkey_blob(privkey_buffer, &key); if (rc == SSH_ERROR){ - goto error; + goto out; } comment = ssh_buffer_get_ssh_string(privkey_buffer); SAFE_FREE(comment); @@ -344,15 +357,15 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, ssh_key_free(key); key = NULL; SSH_LOG(SSH_LOG_WARN, "Invalid padding"); - goto error; + goto out; } } -error: - if(buffer != NULL){ +out: + if (buffer != NULL) { ssh_buffer_free(buffer); buffer = NULL; } - if(privkey_buffer != NULL){ + if (privkey_buffer != NULL) { ssh_buffer_free(privkey_buffer); privkey_buffer = NULL; } @@ -365,6 +378,19 @@ error: return key; } +ssh_key ssh_pki_openssh_privkey_import(const char *text_key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data) +{ + return ssh_pki_openssh_import(text_key, passphrase, auth_fn, auth_data, true); +} + +ssh_key ssh_pki_openssh_pubkey_import(const char *text_key) +{ + return ssh_pki_openssh_import(text_key, NULL, NULL, NULL, false); +} + /** @internal * @brief exports a private key to a string blob.