|
Packit Service |
31306d |
/*
|
|
Packit Service |
31306d |
* pki_container_openssh.c
|
|
Packit Service |
31306d |
* This file is part of the SSH Library
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* Copyright (c) 2013,2014 Aris Adamantiadis <aris@badcode.be>
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* The SSH Library is free software; you can redistribute it and/or modify
|
|
Packit Service |
31306d |
* it under the terms of the GNU Lesser General Public License as published by
|
|
Packit Service |
31306d |
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
|
Packit Service |
31306d |
* option) any later version.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* The SSH Library is distributed in the hope that it will be useful, but
|
|
Packit Service |
31306d |
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
Packit Service |
31306d |
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
Packit Service |
31306d |
* License for more details.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit Service |
31306d |
* along with the SSH Library; see the file COPYING. If not, write to
|
|
Packit Service |
31306d |
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
Packit Service |
31306d |
* MA 02111-1307, USA.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @ingroup libssh_pki
|
|
Packit Service |
31306d |
* *
|
|
Packit Service |
31306d |
* @{
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
#include "config.h"
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
#include <ctype.h>
|
|
Packit Service |
31306d |
#include <string.h>
|
|
Packit Service |
31306d |
#include <stdbool.h>
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
#include "libssh/libssh.h"
|
|
Packit Service |
31306d |
#include "libssh/priv.h"
|
|
Packit Service |
31306d |
#include "libssh/pki.h"
|
|
Packit Service |
31306d |
#include "libssh/pki_priv.h"
|
|
Packit Service |
31306d |
#include "libssh/buffer.h"
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @internal
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @brief Import a private key from a ssh buffer.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] key_blob_buffer The key blob to import as specified in
|
|
Packit Service |
31306d |
* key.c:key_private_serialize in OpenSSH source
|
|
Packit Service |
31306d |
* code.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[out] pkey A pointer where the allocated key can be stored. You
|
|
Packit Service |
31306d |
* need to free the memory.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @return SSH_OK on success, SSH_ERROR on error.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @see ssh_key_free()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
|
|
Packit Service |
31306d |
ssh_key *pkey)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
enum ssh_keytypes_e type;
|
|
Packit Service |
31306d |
char *type_s = NULL;
|
|
Packit Service |
31306d |
ssh_key key = NULL;
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (pkey == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_buffer_unpack(key_blob_buffer, "s", &type_s);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR){
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Unpack error");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
type = ssh_key_type_from_name(type_s);
|
|
Packit Service |
31306d |
if (type == SSH_KEYTYPE_UNKNOWN) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Unknown key type '%s' found!", type_s);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
SAFE_FREE(type_s);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = pki_import_privkey_buffer(type, key_blob_buffer, &key);
|
|
Packit Service |
31306d |
if (rc != SSH_OK) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Failed to read key in OpenSSH format");
|
|
Packit Service |
31306d |
goto fail;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
*pkey = key;
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
fail:
|
|
Packit Service |
31306d |
ssh_key_free(key);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief decrypts an encrypted private key blob in OpenSSH format.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
static int pki_private_key_decrypt(ssh_string blob,
|
|
Packit Service |
31306d |
const char* passphrase,
|
|
Packit Service |
31306d |
const char *ciphername,
|
|
Packit Service |
31306d |
const char *kdfname,
|
|
Packit Service |
31306d |
ssh_string kdfoptions,
|
|
Packit Service |
31306d |
ssh_auth_callback auth_fn,
|
|
Packit Service |
31306d |
void *auth_data)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
struct ssh_cipher_struct *ciphers = ssh_get_ciphertab();
|
|
Packit Service |
31306d |
struct ssh_cipher_struct cipher;
|
|
Packit Service |
31306d |
uint8_t key_material[128] = {0};
|
|
Packit Service |
31306d |
char passphrase_buffer[128] = {0};
|
|
Packit Service |
31306d |
size_t key_material_len;
|
|
Packit Service |
31306d |
ssh_buffer buffer = NULL;
|
|
Packit Service |
31306d |
ssh_string salt = NULL;
|
|
Packit Service |
31306d |
uint32_t rounds;
|
|
Packit Service |
31306d |
int cmp;
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
int i;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
cmp = strcmp(ciphername, "none");
|
|
Packit Service |
31306d |
if (cmp == 0){
|
|
Packit Service |
31306d |
/* no decryption required */
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
for (i = 0; ciphers[i].name != NULL; i++) {
|
|
Packit Service |
31306d |
cmp = strcmp(ciphername, ciphers[i].name);
|
|
Packit Service |
31306d |
if (cmp == 0){
|
|
Packit Service |
31306d |
memcpy(&cipher, &ciphers[i], sizeof(cipher));
|
|
Packit Service |
31306d |
break;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (ciphers[i].name == NULL){
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
cmp = strcmp(kdfname, "bcrypt");
|
|
Packit Service |
31306d |
if (cmp != 0) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
if (ssh_string_len(blob) % cipher.blocksize != 0) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN,
|
|
Packit Service |
31306d |
"Encrypted string not multiple of blocksize: %zu",
|
|
Packit Service |
31306d |
ssh_string_len(blob));
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
buffer = ssh_buffer_new();
|
|
Packit Service |
31306d |
if (buffer == NULL){
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
rc = ssh_buffer_add_data(buffer,
|
|
Packit Service |
31306d |
ssh_string_data(kdfoptions),
|
|
Packit Service |
31306d |
ssh_string_len(kdfoptions));
|
|
Packit Service |
31306d |
if (rc != SSH_ERROR){
|
|
Packit Service |
31306d |
rc = ssh_buffer_unpack(buffer, "Sd", &salt, &rounds);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(buffer);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR){
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* We need material for key (keysize bits / 8) and IV (blocksize) */
|
|
Packit Service |
31306d |
key_material_len = cipher.keysize/8 + cipher.blocksize;
|
|
Packit Service |
31306d |
if (key_material_len > sizeof(key_material)) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Key material too big");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_DEBUG,
|
|
Packit Service |
31306d |
"Decryption: %d key, %d IV, %d rounds, %zu bytes salt",
|
|
Packit Service |
31306d |
cipher.keysize/8,
|
|
Packit Service |
31306d |
cipher.blocksize,
|
|
Packit Service |
31306d |
rounds,
|
|
Packit Service |
31306d |
ssh_string_len(salt));
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (passphrase == NULL) {
|
|
Packit Service |
31306d |
if (auth_fn == NULL) {
|
|
Packit Service |
31306d |
SAFE_FREE(salt);
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "No passphrase provided");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
rc = auth_fn("Passphrase",
|
|
Packit Service |
31306d |
passphrase_buffer,
|
|
Packit Service |
31306d |
sizeof(passphrase_buffer),
|
|
Packit Service |
31306d |
0,
|
|
Packit Service |
31306d |
0,
|
|
Packit Service |
31306d |
auth_data);
|
|
Packit Service |
31306d |
if (rc != SSH_OK) {
|
|
Packit Service |
31306d |
SAFE_FREE(salt);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
passphrase = passphrase_buffer;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = bcrypt_pbkdf(passphrase,
|
|
Packit Service |
31306d |
strlen(passphrase),
|
|
Packit Service |
31306d |
ssh_string_data(salt),
|
|
Packit Service |
31306d |
ssh_string_len(salt),
|
|
Packit Service |
31306d |
key_material,
|
|
Packit Service |
31306d |
key_material_len,
|
|
Packit Service |
31306d |
rounds);
|
|
Packit Service |
31306d |
SAFE_FREE(salt);
|
|
Packit Service |
31306d |
if (rc < 0){
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
explicit_bzero(passphrase_buffer, sizeof(passphrase_buffer));
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
cipher.set_decrypt_key(&cipher,
|
|
Packit Service |
31306d |
key_material,
|
|
Packit Service |
31306d |
key_material + cipher.keysize/8);
|
|
Packit Service |
31306d |
cipher.decrypt(&cipher,
|
|
Packit Service |
31306d |
ssh_string_data(blob),
|
|
Packit Service |
31306d |
ssh_string_data(blob),
|
|
Packit Service |
31306d |
ssh_string_len(blob));
|
|
Packit Service |
31306d |
ssh_cipher_clear(&cipher);
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/** @internal
|
|
Packit Service |
31306d |
* @brief Import a private key in OpenSSH (new) format. This format is
|
|
Packit Service |
31306d |
* typically used with ed25519 keys but can be used for others.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
static ssh_key
|
|
Packit Service |
31306d |
ssh_pki_openssh_import(const char *text_key,
|
|
Packit Service |
31306d |
const char *passphrase,
|
|
Packit Service |
31306d |
ssh_auth_callback auth_fn,
|
|
Packit Service |
31306d |
void *auth_data,
|
|
Packit Service |
31306d |
bool private)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
const char *ptr = text_key;
|
|
Packit Service |
31306d |
const char *end;
|
|
Packit Service |
31306d |
char *base64;
|
|
Packit Service |
31306d |
int cmp;
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
int i;
|
|
Packit Service |
31306d |
ssh_buffer buffer = NULL, privkey_buffer=NULL;
|
|
Packit Service |
31306d |
char *magic = NULL, *ciphername = NULL, *kdfname = NULL;
|
|
Packit Service |
31306d |
uint32_t nkeys = 0, checkint1 = 0, checkint2 = 0xFFFF;
|
|
Packit Service |
31306d |
ssh_string kdfoptions = NULL;
|
|
Packit Service |
31306d |
ssh_string pubkey0 = NULL;
|
|
Packit Service |
31306d |
ssh_string privkeys = NULL;
|
|
Packit Service |
31306d |
ssh_string comment = NULL;
|
|
Packit Service |
31306d |
ssh_key key = NULL;
|
|
Packit Service |
31306d |
uint8_t padding;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
|
|
Packit Service |
31306d |
if (cmp != 0) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no header)");
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
ptr += strlen(OPENSSH_HEADER_BEGIN);
|
|
Packit Service |
31306d |
while(ptr[0] != '\0' && !isspace((int)ptr[0])) {
|
|
Packit Service |
31306d |
ptr++;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
end = strstr(ptr, OPENSSH_HEADER_END);
|
|
Packit Service |
31306d |
if (end == NULL) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)");
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
base64 = malloc(end - ptr + 1);
|
|
Packit Service |
31306d |
if (base64 == NULL) {
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
for (i = 0; ptr < end; ptr++) {
|
|
Packit Service |
31306d |
if (!isspace((int)ptr[0])) {
|
|
Packit Service |
31306d |
base64[i] = ptr[0];
|
|
Packit Service |
31306d |
i++;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
base64[i] = '\0';
|
|
Packit Service |
31306d |
buffer = base64_to_bin(base64);
|
|
Packit Service |
31306d |
SAFE_FREE(base64);
|
|
Packit Service |
31306d |
if (buffer == NULL) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)");
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
rc = ssh_buffer_unpack(buffer, "PssSdSS",
|
|
Packit Service |
31306d |
strlen(OPENSSH_AUTH_MAGIC) + 1,
|
|
Packit Service |
31306d |
&magic,
|
|
Packit Service |
31306d |
&ciphername,
|
|
Packit Service |
31306d |
&kdfname,
|
|
Packit Service |
31306d |
&kdfoptions,
|
|
Packit Service |
31306d |
&nkeys,
|
|
Packit Service |
31306d |
&pubkey0,
|
|
Packit Service |
31306d |
&privkeys);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)");
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC));
|
|
Packit Service |
31306d |
if (cmp != 0) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)");
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_INFO,
|
|
Packit Service |
31306d |
"Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d",
|
|
Packit Service |
31306d |
ciphername,
|
|
Packit Service |
31306d |
kdfname,
|
|
Packit Service |
31306d |
nkeys);
|
|
Packit Service |
31306d |
if (nkeys != 1) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys);
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* If we are interested only in public key do not progress
|
|
Packit Service |
31306d |
* to the key decryption later
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
if (!private) {
|
|
Packit Service |
31306d |
rc = ssh_pki_import_pubkey_blob(pubkey0, &key);
|
|
Packit Service |
31306d |
if (rc != SSH_OK) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Failed to import public key blob");
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
/* in either case we clean up here */
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = pki_private_key_decrypt(privkeys,
|
|
Packit Service |
31306d |
passphrase,
|
|
Packit Service |
31306d |
ciphername,
|
|
Packit Service |
31306d |
kdfname,
|
|
Packit Service |
31306d |
kdfoptions,
|
|
Packit Service |
31306d |
auth_fn,
|
|
Packit Service |
31306d |
auth_data);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
privkey_buffer = ssh_buffer_new();
|
|
Packit Service |
31306d |
if (privkey_buffer == NULL) {
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
ssh_buffer_set_secure(privkey_buffer);
|
|
Packit Service |
31306d |
ssh_buffer_add_data(privkey_buffer,
|
|
Packit Service |
31306d |
ssh_string_data(privkeys),
|
|
Packit Service |
31306d |
ssh_string_len(privkeys));
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR || checkint1 != checkint2) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)");
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
rc = pki_openssh_import_privkey_blob(privkey_buffer, &key);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
comment = ssh_buffer_get_ssh_string(privkey_buffer);
|
|
Packit Service |
31306d |
SAFE_FREE(comment);
|
|
Packit Service |
31306d |
/* verify that the remaining data is correct padding */
|
|
Packit Service |
31306d |
for (i = 1; ssh_buffer_get_len(privkey_buffer) > 0; ++i) {
|
|
Packit Service |
31306d |
ssh_buffer_get_u8(privkey_buffer, &padding);
|
|
Packit Service |
31306d |
if (padding != i) {
|
|
Packit Service |
31306d |
ssh_key_free(key);
|
|
Packit Service |
31306d |
key = NULL;
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Invalid padding");
|
|
Packit Service |
31306d |
goto out;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
out:
|
|
Packit Service |
31306d |
if (buffer != NULL) {
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(buffer);
|
|
Packit Service |
31306d |
buffer = NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
if (privkey_buffer != NULL) {
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(privkey_buffer);
|
|
Packit Service |
31306d |
privkey_buffer = NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
SAFE_FREE(magic);
|
|
Packit Service |
31306d |
SAFE_FREE(ciphername);
|
|
Packit Service |
31306d |
SAFE_FREE(kdfname);
|
|
Packit Service |
31306d |
SAFE_FREE(kdfoptions);
|
|
Packit Service |
31306d |
SAFE_FREE(pubkey0);
|
|
Packit Service |
31306d |
SAFE_FREE(privkeys);
|
|
Packit Service |
31306d |
return key;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
|
|
Packit Service |
31306d |
const char *passphrase,
|
|
Packit Service |
31306d |
ssh_auth_callback auth_fn,
|
|
Packit Service |
31306d |
void *auth_data)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
return ssh_pki_openssh_import(text_key, passphrase, auth_fn, auth_data, true);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
ssh_key ssh_pki_openssh_pubkey_import(const char *text_key)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
return ssh_pki_openssh_import(text_key, NULL, NULL, NULL, false);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/** @internal
|
|
Packit Service |
31306d |
* @brief exports a private key to a string blob.
|
|
Packit Service |
31306d |
* @param[in] privkey private key to convert
|
|
Packit Service |
31306d |
* @param[out] buffer buffer to write the blob in.
|
|
Packit Service |
31306d |
* @returns SSH_OK on success
|
|
Packit Service |
31306d |
* @warning only supports ed25519 key type at the moment.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
static int pki_openssh_export_privkey_blob(const ssh_key privkey,
|
|
Packit Service |
31306d |
ssh_buffer buffer)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (privkey->type != SSH_KEYTYPE_ED25519) {
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Type %s not supported", privkey->type_c);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
if (privkey->ed25519_privkey == NULL ||
|
|
Packit Service |
31306d |
privkey->ed25519_pubkey == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
rc = ssh_buffer_pack(buffer,
|
|
Packit Service |
31306d |
"sdPdPP",
|
|
Packit Service |
31306d |
privkey->type_c,
|
|
Packit Service |
31306d |
(uint32_t)ED25519_KEY_LEN,
|
|
Packit Service |
31306d |
(size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey,
|
|
Packit Service |
31306d |
(uint32_t)(2 * ED25519_KEY_LEN),
|
|
Packit Service |
31306d |
(size_t)ED25519_KEY_LEN, privkey->ed25519_privkey,
|
|
Packit Service |
31306d |
(size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey);
|
|
Packit Service |
31306d |
return rc;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/** @internal
|
|
Packit Service |
31306d |
* @brief encrypts an ed25519 private key blob
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
static int pki_private_key_encrypt(ssh_buffer privkey_buffer,
|
|
Packit Service |
31306d |
const char* passphrase,
|
|
Packit Service |
31306d |
const char *ciphername,
|
|
Packit Service |
31306d |
const char *kdfname,
|
|
Packit Service |
31306d |
ssh_auth_callback auth_fn,
|
|
Packit Service |
31306d |
void *auth_data,
|
|
Packit Service |
31306d |
uint32_t rounds,
|
|
Packit Service |
31306d |
ssh_string salt)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
struct ssh_cipher_struct *ciphers = ssh_get_ciphertab();
|
|
Packit Service |
31306d |
struct ssh_cipher_struct cipher;
|
|
Packit Service |
31306d |
uint8_t key_material[128] = {0};
|
|
Packit Service |
31306d |
size_t key_material_len;
|
|
Packit Service |
31306d |
char passphrase_buffer[128] = {0};
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
int i;
|
|
Packit Service |
31306d |
int cmp;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
cmp = strcmp(ciphername, "none");
|
|
Packit Service |
31306d |
if (cmp == 0){
|
|
Packit Service |
31306d |
/* no encryption required */
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
for (i = 0; ciphers[i].name != NULL; i++) {
|
|
Packit Service |
31306d |
cmp = strcmp(ciphername, ciphers[i].name);
|
|
Packit Service |
31306d |
if (cmp == 0){
|
|
Packit Service |
31306d |
memcpy(&cipher, &ciphers[i], sizeof(cipher));
|
|
Packit Service |
31306d |
break;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (ciphers[i].name == NULL){
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
cmp = strcmp(kdfname, "bcrypt");
|
|
Packit Service |
31306d |
if (cmp != 0){
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
/* We need material for key (keysize bits / 8) and IV (blocksize) */
|
|
Packit Service |
31306d |
key_material_len = cipher.keysize/8 + cipher.blocksize;
|
|
Packit Service |
31306d |
if (key_material_len > sizeof(key_material)){
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Key material too big");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Encryption: %d key, %d IV, %d rounds, %zu bytes salt",
|
|
Packit Service |
31306d |
cipher.keysize/8,
|
|
Packit Service |
31306d |
cipher.blocksize, rounds, ssh_string_len(salt));
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (passphrase == NULL){
|
|
Packit Service |
31306d |
if (auth_fn == NULL){
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "No passphrase provided");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
rc = auth_fn("Passphrase",
|
|
Packit Service |
31306d |
passphrase_buffer,
|
|
Packit Service |
31306d |
sizeof(passphrase_buffer),
|
|
Packit Service |
31306d |
0,
|
|
Packit Service |
31306d |
0,
|
|
Packit Service |
31306d |
auth_data);
|
|
Packit Service |
31306d |
if (rc != SSH_OK){
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
passphrase = passphrase_buffer;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = bcrypt_pbkdf(passphrase,
|
|
Packit Service |
31306d |
strlen(passphrase),
|
|
Packit Service |
31306d |
ssh_string_data(salt),
|
|
Packit Service |
31306d |
ssh_string_len(salt),
|
|
Packit Service |
31306d |
key_material,
|
|
Packit Service |
31306d |
key_material_len,
|
|
Packit Service |
31306d |
rounds);
|
|
Packit Service |
31306d |
if (rc < 0){
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
cipher.set_encrypt_key(&cipher,
|
|
Packit Service |
31306d |
key_material,
|
|
Packit Service |
31306d |
key_material + cipher.keysize/8);
|
|
Packit Service |
31306d |
cipher.encrypt(&cipher,
|
|
Packit Service |
31306d |
ssh_buffer_get(privkey_buffer),
|
|
Packit Service |
31306d |
ssh_buffer_get(privkey_buffer),
|
|
Packit Service |
31306d |
ssh_buffer_get_len(privkey_buffer));
|
|
Packit Service |
31306d |
ssh_cipher_clear(&cipher);
|
|
Packit Service |
31306d |
explicit_bzero(passphrase_buffer, sizeof(passphrase_buffer));
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/** @internal
|
|
Packit Service |
31306d |
* generate an OpenSSH private key (defined in PROTOCOL.key) and output it in text format.
|
|
Packit Service |
31306d |
* @param privkey[in] private key to export
|
|
Packit Service |
31306d |
* @returns an SSH string containing the text representation of the exported key.
|
|
Packit Service |
31306d |
* @warning currently only supports ED25519 key types.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
|
|
Packit Service |
31306d |
const char *passphrase,
|
|
Packit Service |
31306d |
ssh_auth_callback auth_fn,
|
|
Packit Service |
31306d |
void *auth_data)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
ssh_buffer buffer;
|
|
Packit Service |
31306d |
ssh_string str = NULL;
|
|
Packit Service |
31306d |
ssh_string pubkey_s=NULL;
|
|
Packit Service |
31306d |
ssh_buffer privkey_buffer = NULL;
|
|
Packit Service |
31306d |
uint32_t rnd;
|
|
Packit Service |
31306d |
uint32_t rounds = 16;
|
|
Packit Service |
31306d |
ssh_string salt=NULL;
|
|
Packit Service |
31306d |
ssh_string kdf_options=NULL;
|
|
Packit Service |
31306d |
int to_encrypt=0;
|
|
Packit Service |
31306d |
unsigned char *b64;
|
|
Packit Service |
31306d |
uint32_t str_len, len;
|
|
Packit Service |
31306d |
uint8_t padding = 1;
|
|
Packit Service |
31306d |
int ok;
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (privkey == NULL) {
|
|
Packit Service |
31306d |
return NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
if (privkey->type != SSH_KEYTYPE_ED25519){
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_WARN, "Unsupported key type %s", privkey->type_c);
|
|
Packit Service |
31306d |
return NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
if (passphrase != NULL || auth_fn != NULL){
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_INFO, "Enabling encryption for private key export");
|
|
Packit Service |
31306d |
to_encrypt = 1;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
buffer = ssh_buffer_new();
|
|
Packit Service |
31306d |
pubkey_s = pki_publickey_to_blob(privkey);
|
|
Packit Service |
31306d |
if(buffer == NULL || pubkey_s == NULL){
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
ok = ssh_get_random(&rnd, sizeof(rnd), 0);
|
|
Packit Service |
31306d |
if (!ok) {
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
privkey_buffer = ssh_buffer_new();
|
|
Packit Service |
31306d |
if (privkey_buffer == NULL) {
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* checkint1 & 2 */
|
|
Packit Service |
31306d |
rc = ssh_buffer_pack(privkey_buffer,
|
|
Packit Service |
31306d |
"dd",
|
|
Packit Service |
31306d |
rnd,
|
|
Packit Service |
31306d |
rnd);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR){
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = pki_openssh_export_privkey_blob(privkey, privkey_buffer);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR){
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* comment */
|
|
Packit Service |
31306d |
rc = ssh_buffer_pack(privkey_buffer, "s", "" /* comment */);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR){
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* Add padding regardless encryption because it is expected
|
|
Packit Service |
31306d |
* by OpenSSH tools.
|
|
Packit Service |
31306d |
* XXX Using 16 B as we use only AES cipher below anyway.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
while (ssh_buffer_get_len(privkey_buffer) % 16 != 0) {
|
|
Packit Service |
31306d |
rc = ssh_buffer_add_u8(privkey_buffer, padding);
|
|
Packit Service |
31306d |
if (rc < 0) {
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
padding++;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (to_encrypt){
|
|
Packit Service |
31306d |
ssh_buffer kdf_buf;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
kdf_buf = ssh_buffer_new();
|
|
Packit Service |
31306d |
if (kdf_buf == NULL) {
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
salt = ssh_string_new(16);
|
|
Packit Service |
31306d |
if (salt == NULL){
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(kdf_buf);
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
ok = ssh_get_random(ssh_string_data(salt), 16, 0);
|
|
Packit Service |
31306d |
if (!ok) {
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(kdf_buf);
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
ssh_buffer_pack(kdf_buf, "Sd", salt, rounds);
|
|
Packit Service |
31306d |
kdf_options = ssh_string_new(ssh_buffer_get_len(kdf_buf));
|
|
Packit Service |
31306d |
if (kdf_options == NULL){
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(kdf_buf);
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
memcpy(ssh_string_data(kdf_options),
|
|
Packit Service |
31306d |
ssh_buffer_get(kdf_buf),
|
|
Packit Service |
31306d |
ssh_buffer_get_len(kdf_buf));
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(kdf_buf);
|
|
Packit Service |
31306d |
rc = pki_private_key_encrypt(privkey_buffer,
|
|
Packit Service |
31306d |
passphrase,
|
|
Packit Service |
31306d |
"aes128-cbc",
|
|
Packit Service |
31306d |
"bcrypt",
|
|
Packit Service |
31306d |
auth_fn,
|
|
Packit Service |
31306d |
auth_data,
|
|
Packit Service |
31306d |
rounds,
|
|
Packit Service |
31306d |
salt);
|
|
Packit Service |
31306d |
if (rc != SSH_OK){
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
} else {
|
|
Packit Service |
31306d |
kdf_options = ssh_string_new(0);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_buffer_pack(buffer,
|
|
Packit Service |
31306d |
"PssSdSdP",
|
|
Packit Service |
31306d |
(size_t)strlen(OPENSSH_AUTH_MAGIC) + 1, OPENSSH_AUTH_MAGIC,
|
|
Packit Service |
31306d |
to_encrypt ? "aes128-cbc" : "none", /* ciphername */
|
|
Packit Service |
31306d |
to_encrypt ? "bcrypt" : "none", /* kdfname */
|
|
Packit Service |
31306d |
kdf_options, /* kdfoptions */
|
|
Packit Service |
31306d |
(uint32_t) 1, /* nkeys */
|
|
Packit Service |
31306d |
pubkey_s,
|
|
Packit Service |
31306d |
(uint32_t)ssh_buffer_get_len(privkey_buffer),
|
|
Packit Service |
31306d |
/* rest of buffer is a string */
|
|
Packit Service |
31306d |
(size_t)ssh_buffer_get_len(privkey_buffer), ssh_buffer_get(privkey_buffer));
|
|
Packit Service |
31306d |
if (rc != SSH_OK) {
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
b64 = bin_to_base64(ssh_buffer_get(buffer),
|
|
Packit Service |
31306d |
ssh_buffer_get_len(buffer));
|
|
Packit Service |
31306d |
if (b64 == NULL){
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* we can reuse the buffer */
|
|
Packit Service |
31306d |
ssh_buffer_reinit(buffer);
|
|
Packit Service |
31306d |
rc = ssh_buffer_pack(buffer,
|
|
Packit Service |
31306d |
"tttttt",
|
|
Packit Service |
31306d |
OPENSSH_HEADER_BEGIN,
|
|
Packit Service |
31306d |
"\n",
|
|
Packit Service |
31306d |
b64,
|
|
Packit Service |
31306d |
"\n",
|
|
Packit Service |
31306d |
OPENSSH_HEADER_END,
|
|
Packit Service |
31306d |
"\n");
|
|
Packit Service |
31306d |
explicit_bzero(b64, strlen((char *)b64));
|
|
Packit Service |
31306d |
SAFE_FREE(b64);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (rc != SSH_OK){
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
str = ssh_string_new(ssh_buffer_get_len(buffer));
|
|
Packit Service |
31306d |
if (str == NULL){
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
str_len = ssh_buffer_get_len(buffer);
|
|
Packit Service |
31306d |
len = ssh_buffer_get_data(buffer, ssh_string_data(str), str_len);
|
|
Packit Service |
31306d |
if (str_len != len) {
|
|
Packit Service |
31306d |
SSH_STRING_FREE(str);
|
|
Packit Service |
31306d |
str = NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
error:
|
|
Packit Service |
31306d |
if (privkey_buffer != NULL) {
|
|
Packit Service |
31306d |
void *bufptr = ssh_buffer_get(privkey_buffer);
|
|
Packit Service |
31306d |
explicit_bzero(bufptr, ssh_buffer_get_len(privkey_buffer));
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(privkey_buffer);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
SAFE_FREE(pubkey_s);
|
|
Packit Service |
31306d |
SAFE_FREE(kdf_options);
|
|
Packit Service |
31306d |
SAFE_FREE(salt);
|
|
Packit Service |
31306d |
if (buffer != NULL) {
|
|
Packit Service |
31306d |
SSH_BUFFER_FREE(buffer);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return str;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @}
|
|
Packit Service |
31306d |
*/
|