Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 47 additions & 47 deletions src/server_config_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,42 +794,37 @@ nc_server_config_util_get_privkey_format(const char *privkey, enum nc_privkey_fo
return 0;
}

/**
* @brief Get private key using the TLS backend's functions.
*
* @param[in] privkey_path Path to the private key file.
* @param[out] pkey TLS backend's underlying private key structure.
* @return 0 on success, 1 on failure.
*/
static int
nc_server_config_util_get_privkey_libtls(const char *privkey_path, char **privkey, void **pkey)
nc_server_config_util_get_privkey_libtls(const char *privkey_path, void **pkey)
{
void *pkey_tmp;
char *privkey_tmp;

NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1);

*privkey = *pkey = NULL;

pkey_tmp = nc_tls_import_privkey_file_wrap(privkey_path);
if (!pkey_tmp) {
return 1;
}

privkey_tmp = nc_tls_export_privkey_pem_wrap(pkey_tmp);
if (!privkey_tmp) {
nc_tls_privkey_destroy_wrap(pkey_tmp);
return 1;
}
*pkey = NULL;

*privkey = privkey_tmp;
*pkey = pkey_tmp;
return 0;
*pkey = nc_tls_import_privkey_file_wrap(privkey_path);
return *pkey ? 0 : 1;
}

/**
* @brief Get private key using libssh functions.
*
* @param[in] privkey_path Path to the private key file.
* @param[out] pkey TLS backend's underlying private key structure.
* @return 0 on success, 1 on failure.
*/
static int
nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privkey, void **pkey)
nc_server_config_util_get_privkey_libssh(const char *privkey_path, void **pkey)
{
int ret = 0;
ssh_key key = NULL;
void *pkey_tmp = NULL;
char *privkey_tmp = NULL;

NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1);
char *privkey_buf = NULL;

/* import the OpenSSH private key using libssh */
ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &key);
if (ret) {
ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
Expand All @@ -838,45 +833,43 @@ nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privke
}

/* export the key in PEM */
ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &privkey_tmp);
ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &privkey_buf);
if (ret) {
ERR(NULL, "Exporting privkey to base64 failed.");
goto cleanup;
}

pkey_tmp = nc_tls_pem_to_privkey_wrap(privkey_tmp);
if (!pkey_tmp) {
free(privkey_tmp);
/* convert the base64 PEM to libtls private key representation */
*pkey = nc_tls_pem_to_privkey_wrap(privkey_buf);
if (!*pkey) {
ret = 1;
goto cleanup;
}

*privkey = privkey_tmp;
*pkey = pkey_tmp;

cleanup:
free(privkey_buf);
ssh_key_free(key);
return ret;
}

static int
nc_server_config_util_pem_strip_header_footer(const char *pem, char **privkey)
nc_server_config_util_privkey_strip_header_footer(const char *orig_privkey, char **privkey)
{
const char *header, *footer;

if (!strncmp(pem, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
if (!strncmp(orig_privkey, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
/* it's PKCS8 (X.509) private key */
header = NC_PKCS8_PRIVKEY_HEADER;
footer = NC_PKCS8_PRIVKEY_FOOTER;
} else if (!strncmp(pem, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
} else if (!strncmp(orig_privkey, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
/* it's OpenSSH private key */
header = NC_OPENSSH_PRIVKEY_HEADER;
footer = NC_OPENSSH_PRIVKEY_FOOTER;
} else if (!strncmp(pem, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
} else if (!strncmp(orig_privkey, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
/* it's RSA privkey in PKCS1 format */
header = NC_PKCS1_RSA_PRIVKEY_HEADER;
footer = NC_PKCS1_RSA_PRIVKEY_FOOTER;
} else if (!strncmp(pem, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
} else if (!strncmp(orig_privkey, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
/* it's EC privkey in SEC1 format */
header = NC_SEC1_EC_PRIVKEY_HEADER;
footer = NC_SEC1_EC_PRIVKEY_FOOTER;
Expand All @@ -885,7 +878,7 @@ nc_server_config_util_pem_strip_header_footer(const char *pem, char **privkey)
}

/* make a copy without the header and footer */
*privkey = strndup(pem + strlen(header), strlen(pem) - strlen(header) - strlen(footer));
*privkey = strndup(orig_privkey + strlen(header), strlen(orig_privkey) - strlen(header) - strlen(footer));
NC_CHECK_ERRMEM_RET(!*privkey, 1);

return 0;
Expand Down Expand Up @@ -930,13 +923,11 @@ nc_server_config_util_get_privkey(const char *privkey_path, enum nc_privkey_form
case NC_PRIVKEY_FORMAT_EC:
case NC_PRIVKEY_FORMAT_X509:
/* the TLS lib can do this */
ret = nc_server_config_util_get_privkey_libtls(privkey_path, &priv, pkey);
ret = nc_server_config_util_get_privkey_libtls(privkey_path, pkey);
break;
case NC_PRIVKEY_FORMAT_OPENSSH:
/* need the help of libssh */
ret = nc_server_config_util_get_privkey_libssh(privkey_path, &priv, pkey);
/* if the function returned successfully, the key is no longer OpenSSH, it was converted to x509 */
*privkey_format = NC_PRIVKEY_FORMAT_X509;
ret = nc_server_config_util_get_privkey_libssh(privkey_path, pkey);
break;
default:
ERR(NULL, "Private key format not recognized.");
Expand All @@ -947,17 +938,26 @@ nc_server_config_util_get_privkey(const char *privkey_path, enum nc_privkey_form
goto cleanup;
}

/* parsing may have changed its type, get it again */
/* export the private key to its original format type,
* all of this was done to avoid having to parse the private key ourselves
* and since we have a "pkey" we can be sure, that the private key is valid */
ret = nc_tls_privkey_export_wrap(*pkey, *privkey_format, &priv);
if (ret) {
goto cleanup;
}

/* get the privkey format again from the exported private key,
* it should match the previous one, but in case it doesn't,
* we can still at least store the 'current' one in YANG and use it */
ret = nc_server_config_util_get_privkey_format(priv, privkey_format);
if (ret) {
ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path);
ERR(NULL, "Private key format \"%s\" not supported.", priv);
goto cleanup;
}

/* strip private key's header and footer */
ret = nc_server_config_util_pem_strip_header_footer(priv, privkey);
ret = nc_server_config_util_privkey_strip_header_footer(priv, privkey);
if (ret) {
ERR(NULL, "Stripping header and footer from private key \"%s\" failed.", privkey_path);
goto cleanup;
}

Expand Down
95 changes: 81 additions & 14 deletions src/session_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
#include "session_p.h"
#include "session_wrapper.h"

#include <libssh/libssh.h>

#include <mbedtls/base64.h>
#include <mbedtls/bignum.h>
#include <mbedtls/ctr_drbg.h>
Expand Down Expand Up @@ -1411,28 +1413,93 @@ nc_tls_import_cert_file_wrap(const char *cert_path)
return c;
}

char *
nc_tls_export_privkey_pem_wrap(void *pkey)
/**
* @brief Convert a PKCS#1/SEC1 private key to OpenSSH format.
*
* @param[in] pk Private key in PKCS#1/SEC1 PEM format.
* @param[out] privkey Private key in OpenSSH format.
* @return 0 on success, 1 on error.
*/
static int
nc_tls_privkey_export_openssh(const char *pk, char **privkey)
{
int rc;
char *pem;
int rc = 0;
ssh_key sshkey = NULL;

*privkey = NULL;

/* older versions of libssh (< v0.11.0) do not support exporting to OpenSSH format,
* signal this to the caller by returning success with NULL privkey */
#if (LIBSSH_VERSION_MAJOR > 0) || (LIBSSH_VERSION_MAJOR == 0 && LIBSSH_VERSION_MINOR >= 11)
/* load the SEC1/PKCS#1 using libssh */
if (ssh_pki_import_privkey_base64(pk, NULL, NULL, NULL, &sshkey)) {
ERR(NULL, "Importing the private key to libssh failed (%s).", ssh_get_error(NULL));
rc = 1;
goto cleanup;
}

/* export to OpenSSH format */
if (ssh_pki_export_privkey_base64_format(sshkey, NULL, NULL, NULL, privkey, SSH_FILE_FORMAT_OPENSSH)) {
ERR(NULL, "Exporting the private key to OpenSSH format failed (%s).", ssh_get_error(NULL));
rc = 1;
goto cleanup;
}
#endif // (LIBSSH_VERSION_MAJOR > 0) || (LIBSSH_VERSION_MAJOR == 0 && LIBSSH_VERSION_MINOR >= 11)

cleanup:
ssh_key_free(sshkey);
return rc;
}

int
nc_tls_privkey_export_wrap(void *pkey, enum nc_privkey_format format, char **privkey)
{
int r, rc = 0;
size_t size = 128;
char *pk;

pem = malloc(size);
NC_CHECK_ERRMEM_RET(!pem, NULL);
if (format == NC_PRIVKEY_FORMAT_UNKNOWN) {
ERRINT;
return 1;
}

/* use mbedtls_pk_write_key_pem to write either PKCS#1 or SEC1 format */
pk = malloc(size);
NC_CHECK_ERRMEM_RET(!pk, 1);

while ((rc = mbedtls_pk_write_key_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
/* try to write the key, reallocating if the buffer is too small */
while ((r = mbedtls_pk_write_key_pem(pkey,
(unsigned char *)pk, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
size <<= 1;
pem = nc_realloc(pem, size);
NC_CHECK_ERRMEM_RET(!pem, NULL);
pk = nc_realloc(pk, size);
NC_CHECK_ERRMEM_RET(!pk, 1);
}
if (rc < 0) {
nc_mbedtls_strerr(NULL, rc, "Exporting private key to PEM format failed");
free(pem);
return NULL;
if (r < 0) {
nc_mbedtls_strerr(NULL, r, "Exporting private key to PEM format failed");
rc = 1;
goto cleanup;
}

return pem;
if (format == NC_PRIVKEY_FORMAT_OPENSSH) {
rc = nc_tls_privkey_export_openssh(pk, privkey);
if (rc) {
goto cleanup;
}

if (!*privkey) {
/* privkey not converted, just use the PEM as is (PKCS#1 or SEC1) */
*privkey = pk;
pk = NULL;
}
} else {
/* return the PEM as is (PKCS#1 or SEC1), mbedtls can not do NC_PRIVKEY_FORMAT_X509 */
*privkey = pk;
pk = NULL;
}

cleanup:
free(pk);
return rc;
}

char *
Expand Down
Loading