config UPDATE use correct public key types
diff --git a/src/config_new.c b/src/config_new.c
index 4cefadf..fa8dca2 100644
--- a/src/config_new.c
+++ b/src/config_new.c
@@ -210,7 +210,7 @@
case NC_PRIVKEY_FORMAT_EC:
return "ietf-crypto-types:ec-private-key-format";
case NC_PRIVKEY_FORMAT_X509:
- return "libnetconf2-netconf-server:subject-private-key-info-format";
+ return "libnetconf2-netconf-server:private-key-info-format";
case NC_PRIVKEY_FORMAT_OPENSSH:
return "libnetconf2-netconf-server:openssh-private-key-format";
default:
@@ -219,6 +219,306 @@
}
}
+static int
+nc_server_config_new_pubkey_bin_to_b64(const unsigned char *pub_bin, int bin_len, char **pubkey)
+{
+ int ret = 0, b64_len;
+ char *pub_b64 = NULL;
+
+ /* get b64 buffer len, for ever 3 bytes of bin 4 bytes of b64 + NULL terminator */
+ if (bin_len % 3 == 0) {
+ pub_b64 = malloc((bin_len / 3) * 4 + 1);
+ } else {
+ /* bin len not divisible by 3, need to add 4 bytes for some padding so that the len is divisible by 4 */
+ pub_b64 = malloc((bin_len / 3) * 4 + 4 + 1);
+ }
+ if (!pub_b64) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* bin to b64 */
+ b64_len = EVP_EncodeBlock((unsigned char *)pub_b64, pub_bin, bin_len);
+ *pubkey = strndup(pub_b64, b64_len);
+ if (!*pubkey) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+cleanup:
+ free(pub_b64);
+ return ret;
+}
+
+static int
+nc_server_config_new_bn_to_bin(const BIGNUM *bn, unsigned char **bin, int *bin_len)
+{
+ int ret = 0;
+ unsigned char *bin_tmp = NULL;
+
+ NC_CHECK_ARG_RET(NULL, bn, bin, bin_len, 1);
+
+ *bin = NULL;
+
+ /* prepare buffer for converting BN to binary */
+ bin_tmp = calloc(BN_num_bytes(bn), sizeof *bin_tmp);
+ if (!bin_tmp) {
+ ERRMEM;
+ return 1;
+ }
+
+ /* convert to binary */
+ *bin_len = BN_bn2bin(bn, bin_tmp);
+
+ /* if the highest bit in the MSB is set a byte with the value 0 has to be prepended */
+ if (bin_tmp[0] & 0x80) {
+ *bin = malloc(*bin_len + 1);
+ if (!*bin) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+ (*bin)[0] = 0;
+ memcpy(*bin + 1, bin_tmp, *bin_len);
+ (*bin_len)++;
+ } else {
+ *bin = malloc(*bin_len);
+ if (!*bin) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+ memcpy(*bin, bin_tmp, *bin_len);
+ }
+
+cleanup:
+ free(bin_tmp);
+ return ret;
+}
+
+/* ssh pubkey defined in RFC 4253 section 6.6 */
+static int
+nc_server_config_new_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey)
+{
+ int ret = 0, e_len, n_len, p_len, bin_len;
+ BIGNUM *e = NULL, *n = NULL, *p = NULL;
+ unsigned char *e_bin = NULL, *n_bin = NULL, *p_bin = NULL, *bin = NULL, *bin_tmp;
+ const char *algorithm_name, *curve_name;
+ char *ec_group = NULL;
+ uint32_t alg_name_len, curve_name_len, alg_name_len_be, curve_name_len_be, p_len_be, e_len_be, n_len_be;
+ size_t ec_group_len;
+
+ if (EVP_PKEY_is_a(pkey, "RSA")) {
+ /* RSA key */
+ algorithm_name = "ssh-rsa";
+
+ /* get the public key params */
+ if (!EVP_PKEY_get_bn_param(pkey, "e", &e) || !EVP_PKEY_get_bn_param(pkey, "n", &n)) {
+ ERR(NULL, "Getting public key parameters from RSA private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* BIGNUM to bin */
+ if (nc_server_config_new_bn_to_bin(e, &e_bin, &e_len) || nc_server_config_new_bn_to_bin(n, &n_bin, &n_len)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ alg_name_len = strlen(algorithm_name);
+ /* buffer for public key in binary, which looks like this:
+ * alg_name len (4 bytes), alg_name, PK exponent len (4 bytes), PK exponent, modulus len (4 bytes), modulus
+ */
+ bin_len = 4 + alg_name_len + 4 + e_len + 4 + n_len;
+ bin = malloc(bin_len);
+ if (!bin) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* to network byte order (big endian) */
+ alg_name_len_be = htonl(alg_name_len);
+ e_len_be = htonl(e_len);
+ n_len_be = htonl(n_len);
+
+ /* create the public key in binary */
+ bin_tmp = bin;
+ memcpy(bin_tmp, &alg_name_len_be, 4);
+ bin_tmp += 4;
+ memcpy(bin_tmp, algorithm_name, alg_name_len);
+ bin_tmp += alg_name_len;
+ memcpy(bin_tmp, &e_len_be, 4);
+ bin_tmp += 4;
+ memcpy(bin_tmp, e_bin, e_len);
+ bin_tmp += e_len;
+ memcpy(bin_tmp, &n_len_be, 4);
+ bin_tmp += 4;
+ memcpy(bin_tmp, n_bin, n_len);
+ } else if (EVP_PKEY_is_a(pkey, "EC")) {
+ /* EC Private key, get it's group first */
+ /* get group len */
+ ret = EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &ec_group_len);
+ if (!ret) {
+ ret = 1;
+ goto cleanup;
+ }
+ /* alloc mem for group + 1 for \0 */
+ ec_group = malloc(ec_group_len + 1);
+ if (!ec_group) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+ /* get the group */
+ ret = EVP_PKEY_get_utf8_string_param(pkey, "group", ec_group, ec_group_len + 1, NULL);
+ if (!ret) {
+ ERR(NULL, "Getting public key parameter from EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* get alg and curve names */
+ if (!strcmp(ec_group, "P-256") || !strcmp(ec_group, "secp256r1") || !strcmp(ec_group, "prime256v1")) {
+ algorithm_name = "ecdsa-sha2-nistp256";
+ curve_name = "nistp256";
+ } else if (!strcmp(ec_group, "P-384") || !strcmp(ec_group, "secp384r1")) {
+ algorithm_name = "ecdsa-sha2-nistp384";
+ curve_name = "nistp384";
+ } else if (!strcmp(ec_group, "P-521") || !strcmp(ec_group, "secp521r1")) {
+ algorithm_name = "ecdsa-sha2-nistp521";
+ curve_name = "nistp521";
+ } else {
+ ERR(NULL, "EC group \"%s\" not supported.", ec_group);
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* get the public key - p, which is a point on the elliptic curve */
+ ret = EVP_PKEY_get_bn_param(pkey, "p", &p);
+ if (!ret) {
+ ERR(NULL, "Getting public key point from the EC private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* prepare buffer for converting p to binary */
+ p_bin = malloc(BN_num_bytes(p));
+ if (!p_bin) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+ /* convert to binary */
+ p_len = BN_bn2bin(p, p_bin);
+
+ alg_name_len = strlen(algorithm_name);
+ curve_name_len = strlen(curve_name);
+ /* buffer for public key in binary, which looks like so:
+ * alg_name len (4 bytes), alg_name, curve_name len (4 bytes), curve_name, PK point p len (4 bytes), PK point p
+ */
+ bin_len = 4 + alg_name_len + 4 + curve_name_len + 4 + p_len;
+ bin = malloc(bin_len);
+ if (!bin) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* to network byte order (big endian) */
+ alg_name_len_be = htonl(alg_name_len);
+ curve_name_len_be = htonl(curve_name_len);
+ p_len_be = htonl(p_len);
+
+ /* create the public key in binary */
+ bin_tmp = bin;
+ memcpy(bin_tmp, &alg_name_len_be, 4);
+ bin_tmp += 4;
+ memcpy(bin_tmp, algorithm_name, alg_name_len);
+ bin_tmp += alg_name_len;
+ memcpy(bin_tmp, &curve_name_len_be, 4);
+ bin_tmp += 4;
+ memcpy(bin_tmp, curve_name, curve_name_len);
+ bin_tmp += curve_name_len;
+ memcpy(bin_tmp, &p_len_be, 4);
+ bin_tmp += 4;
+ memcpy(bin_tmp, p_bin, p_len);
+ } else if (EVP_PKEY_is_a(pkey, "ED25519")) {
+ ERR(NULL, "Generating PEM ED25519 key from OpenSSH is not supported by libssh yet.");
+ ret = 1;
+ goto cleanup;
+ } else {
+ ERR(NULL, "Unable to generate public key from private key (Private key type not supported).");
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = nc_server_config_new_pubkey_bin_to_b64(bin, bin_len, pubkey);
+ if (ret) {
+ ERR(NULL, "Converting public key from binary to base64 failed.");
+ goto cleanup;
+ }
+
+cleanup:
+ free(bin);
+ free(e_bin);
+ free(n_bin);
+ free(ec_group);
+ free(p_bin);
+ BN_free(e);
+ BN_free(n);
+ BN_free(p);
+ return ret;
+}
+
+/* spki = subject public key info */
+static int
+nc_server_config_new_evp_pkey_to_spki_pubkey(EVP_PKEY *pkey, char **pubkey)
+{
+ int ret = 0, len;
+ BIO *bio = NULL;
+ char *pub_b64 = NULL;
+
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* write the evp_pkey contents to bio */
+ if (!PEM_write_bio_PUBKEY(bio, pkey)) {
+ ERR(NULL, "Writing public key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* read the pubkey from bio */
+ len = BIO_get_mem_data(bio, &pub_b64);
+ if (len <= 0) {
+ ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* copy the public key without the header and footer */
+ *pubkey = strndup(pub_b64 + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER),
+ len - strlen(NC_SUBJECT_PUBKEY_INFO_HEADER) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER));
+ if (!*pubkey) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
+ }
+
+cleanup:
+ BIO_free(bio);
+ return ret;
+}
+
int
nc_server_config_new_read_certificate(const char *cert_path, char **cert)
{
@@ -305,7 +605,7 @@
}
static int
-nc_server_config_new_read_ssh2_pubkey(FILE *f, char **pubkey)
+nc_server_config_new_read_pubkey_ssh2(FILE *f, char **pubkey)
{
char *buffer = NULL;
size_t size = 0, pubkey_len = 0;
@@ -355,74 +655,20 @@
nc_server_config_new_read_pubkey_openssl(FILE *f, char **pubkey)
{
int ret = 0;
- EVP_PKEY *pkey = NULL;
- BIO *bio = NULL;
- char *key = NULL;
- int pub_len;
+ EVP_PKEY *pub_pkey = NULL;
+
+ NC_CHECK_ARG_RET(NULL, f, pubkey, 1);
/* read the pubkey from file */
- pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
- if (!pkey) {
- ret = -1;
- goto cleanup;
+ pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
+ if (!pub_pkey) {
+ ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ return 1;
}
- bio = BIO_new(BIO_s_mem());
- if (!bio) {
- ret = -1;
- goto cleanup;
- }
+ ret = nc_server_config_new_evp_pkey_to_ssh_pubkey(pub_pkey, pubkey);
- /* write the pubkey into bio */
- ret = PEM_write_bio_PUBKEY(bio, pkey);
- if (!ret) {
- ret = -1;
- goto cleanup;
- }
-
- pub_len = BIO_pending(bio);
- if (pub_len <= 0) {
- ret = -1;
- goto cleanup;
- }
-
- /* get pubkey's length */
- key = malloc(pub_len + 1);
- if (!key) {
- ERRMEM;
- ret = 1;
- goto cleanup;
- }
-
- /* read the public key from bio */
- ret = BIO_read(bio, key, pub_len);
- if (ret <= 0) {
- ret = -1;
- goto cleanup;
- }
- key[pub_len] = '\0';
-
- /* strip the pubkey of the header and footer */
- *pubkey = strdup(key + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER));
- if (!*pubkey) {
- ERRMEM;
- ret = 1;
- goto cleanup;
- }
-
- (*pubkey)[strlen(*pubkey) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)] = '\0';
-
- ret = 0;
-cleanup:
- if (ret == -1) {
- ERR(NULL, "Error getting public key from file (OpenSSL Error): \"%s\".", ERR_reason_error_string(ERR_get_error()));
- ret = 1;
- }
-
- BIO_free(bio);
- EVP_PKEY_free(pkey);
- free(key);
-
+ EVP_PKEY_free(pub_pkey);
return ret;
}
@@ -432,6 +678,8 @@
int ret = 0;
ssh_key pub_sshkey = NULL;
+ NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
+
ret = ssh_pki_import_pubkey_file(pubkey_path, &pub_sshkey);
if (ret) {
ERR(NULL, "Importing public key from file \"%s\" failed.", pubkey_path);
@@ -440,22 +688,24 @@
ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
if (ret) {
- ERR(NULL, "Exporting public key to base64 failed.");
+ ERR(NULL, "Importing pubkey failed.");
+ goto cleanup;
}
+cleanup:
ssh_key_free(pub_sshkey);
- return ret;
+ return 0;
}
int
-nc_server_config_new_get_pubkey(const char *pubkey_path, char **pubkey, NC_PUBKEY_FORMAT *pubkey_type)
+nc_server_config_new_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey)
{
int ret = 0;
FILE *f = NULL;
char *header = NULL;
size_t len = 0;
- NC_CHECK_ARG_RET(NULL, pubkey, pubkey_type, 1);
+ NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
*pubkey = NULL;
@@ -466,6 +716,7 @@
goto cleanup;
}
+ /* read the header */
if (getline(&header, &len, f) < 0) {
ERR(NULL, "Error reading header from file \"%s\".", pubkey_path);
ret = 1;
@@ -476,17 +727,13 @@
if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) {
/* it's subject public key info public key */
ret = nc_server_config_new_read_pubkey_openssl(f, pubkey);
- *pubkey_type = NC_PUBKEY_FORMAT_X509;
} else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) {
/* it's ssh2 public key */
- ret = nc_server_config_new_read_ssh2_pubkey(f, pubkey);
- *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
+ ret = nc_server_config_new_read_pubkey_ssh2(f, pubkey);
} else {
/* it's probably OpenSSH public key */
ret = nc_server_config_new_read_pubkey_libssh(pubkey_path, pubkey);
- *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
}
-
if (ret) {
ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path);
goto cleanup;
@@ -498,199 +745,190 @@
}
free(header);
-
- return ret;
-}
-
-static int
-nc_server_config_new_get_privkey_openssl(FILE *f, char **privkey, EVP_PKEY **priv_pkey)
-{
- int ret = 0, priv_len;
- BIO *bio = NULL;
-
- NC_CHECK_ARG_RET(NULL, privkey, priv_pkey, 1);
-
- /* read private key from file */
- *priv_pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL);
- if (!*priv_pkey) {
- ret = -1;
- goto cleanup;
- }
-
- bio = BIO_new(BIO_s_mem());
- if (!bio) {
- ret = -1;
- goto cleanup;
- }
-
- /* write the private key in to bio */
- ret = PEM_write_bio_PrivateKey(bio, *priv_pkey, NULL, NULL, 0, NULL, NULL);
- if (!ret) {
- ret = -1;
- goto cleanup;
- }
-
- priv_len = BIO_pending(bio);
- if (priv_len <= 0) {
- ret = -1;
- goto cleanup;
- }
-
- /* get private key's length */
- *privkey = malloc(priv_len + 1);
- if (!*privkey) {
- ERRMEM;
- ret = 1;
- goto cleanup;
- }
-
- /* read the private key from bio */
- ret = BIO_read(bio, *privkey, priv_len);
- if (ret <= 0) {
- ret = -1;
- goto cleanup;
- }
- (*privkey)[priv_len] = '\0';
-
- ret = 0;
-cleanup:
- if (ret < 0) {
- ERR(NULL, "Getting private key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
- }
- BIO_free(bio);
- return ret;
-}
-
-static int
-nc_server_config_new_privkey_to_pubkey_openssl(EVP_PKEY *priv_pkey, char **pubkey)
-{
- int ret = 0, pub_len;
- BIO *bio = NULL;
-
- bio = BIO_new(BIO_s_mem());
- if (!bio) {
- ret = -1;
- goto cleanup;
- }
-
- /* write the pubkey into bio */
- ret = PEM_write_bio_PUBKEY(bio, priv_pkey);
- if (!ret) {
- ret = -1;
- goto cleanup;
- }
-
- /* get the length of the pubkey */
- pub_len = BIO_pending(bio);
- if (pub_len <= 0) {
- ret = -1;
- goto cleanup;
- }
-
- *pubkey = malloc(pub_len + 1);
- if (!*pubkey) {
- ERRMEM;
- ret = 1;
- goto cleanup;
- }
-
- /* read the pubkey from the bio */
- ret = BIO_read(bio, *pubkey, pub_len);
- if (ret <= 0) {
- ret = -1;
- goto cleanup;
- }
- (*pubkey)[pub_len] = '\0';
-
- ret = 0;
-
-cleanup:
- if (ret < 0) {
- ERR(NULL, "Converting private to public key failed (%s).", ERR_reason_error_string(ERR_get_error()));
- }
- BIO_free(bio);
- return ret;
-}
-
-static int
-nc_server_config_new_privkey_to_pubkey_libssh(const ssh_key priv_sshkey, char **pubkey)
-{
- int ret;
- ssh_key pub_sshkey = NULL;
-
- ret = ssh_pki_export_privkey_to_pubkey(priv_sshkey, &pub_sshkey);
- if (ret) {
- ERR(NULL, "Exporting privkey to pubkey failed.");
- return ret;
- }
-
- ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey);
- if (ret) {
- ERR(NULL, "Exporting pubkey to base64 failed.");
- }
-
- ssh_key_free(pub_sshkey);
- return ret;
-}
-
-static int
-nc_server_config_new_privkey_to_pubkey(EVP_PKEY *priv_pkey, const ssh_key priv_sshkey, NC_PRIVKEY_FORMAT privkey_type, char **pubkey, NC_PUBKEY_FORMAT *pubkey_type)
-{
- switch (privkey_type) {
- case NC_PRIVKEY_FORMAT_RSA:
- case NC_PRIVKEY_FORMAT_EC:
- case NC_PRIVKEY_FORMAT_OPENSSH:
- *pubkey_type = NC_PUBKEY_FORMAT_SSH2;
- return nc_server_config_new_privkey_to_pubkey_libssh(priv_sshkey, pubkey);
- case NC_PRIVKEY_FORMAT_X509:
- *pubkey_type = NC_PUBKEY_FORMAT_X509;
- return nc_server_config_new_privkey_to_pubkey_openssl(priv_pkey, pubkey);
- default:
- break;
- }
-
- return 1;
-}
-
-static int
-nc_server_config_new_get_privkey_libssh(const char *privkey_path, char **privkey, ssh_key *priv_sshkey)
-{
- int ret;
-
- *priv_sshkey = NULL;
-
- ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, priv_sshkey);
- if (ret) {
- ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
- return ret;
- }
-
- ret = ssh_pki_export_privkey_base64(*priv_sshkey, NULL, NULL, NULL, privkey);
- if (ret) {
- ERR(NULL, "Exporting privkey from file \"%s\" to base64 failed.", privkey_path);
- }
-
return ret;
}
int
-nc_server_config_new_get_keys(const char *privkey_path, const char *pubkey_path,
- char **privkey, char **pubkey, NC_PRIVKEY_FORMAT *privkey_type, NC_PUBKEY_FORMAT *pubkey_type)
+nc_server_config_new_get_spki_pubkey_file(const char *pubkey_path, char **pubkey)
{
int ret = 0;
- EVP_PKEY *priv_pkey = NULL;
- ssh_key priv_sshkey = NULL;
- FILE *f_privkey = NULL;
- char *header = NULL;
- size_t len = 0;
- char *priv = NULL, *pub = NULL;
+ FILE *f = NULL;
+ EVP_PKEY *pub_pkey = NULL;
- NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pubkey, privkey_type, 1);
+ NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1);
- *privkey = NULL;
*pubkey = NULL;
- /* get private key first */
+ f = fopen(pubkey_path, "r");
+ if (!f) {
+ ERR(NULL, "Unable to open file \"%s\".", pubkey_path);
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* read the pubkey from file */
+ pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
+ if (!pub_pkey) {
+ ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ return 1;
+ }
+
+ ret = nc_server_config_new_evp_pkey_to_spki_pubkey(pub_pkey, pubkey);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ if (f) {
+ fclose(f);
+ }
+
+ EVP_PKEY_free(pub_pkey);
+ return ret;
+}
+
+static int
+nc_server_config_new_privkey_header_to_format(FILE *f_privkey, const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format)
+{
+ char *privkey_header = NULL;
+ size_t len = 0;
+
+ /* read header */
+ if (getline(&privkey_header, &len, f_privkey) < 0) {
+ ERR(NULL, "Error reading header from file \"%s\".", privkey_path);
+ return 1;
+ }
+
+ if (!strncmp(privkey_header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
+ /* it's PKCS8 (X.509) private key */
+ *privkey_format = NC_PRIVKEY_FORMAT_X509;
+ } else if (!strncmp(privkey_header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
+ /* it's OpenSSH private key */
+ *privkey_format = NC_PRIVKEY_FORMAT_OPENSSH;
+ } else if (!strncmp(privkey_header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
+ /* it's RSA privkey in PKCS1 format */
+ *privkey_format = NC_PRIVKEY_FORMAT_RSA;
+ } else if (!strncmp(privkey_header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
+ /* it's EC privkey in SEC1 format */
+ *privkey_format = NC_PRIVKEY_FORMAT_EC;
+ } else {
+ ERR(NULL, "Private key format (%s) not supported.", privkey_header);
+ free(privkey_header);
+ return 1;
+ }
+
+ /* reset the reading head */
+ rewind(f_privkey);
+ free(privkey_header);
+ return 0;
+}
+
+static int
+nc_server_config_new_get_privkey_openssl(const char *privkey_path, FILE *f_privkey, char **privkey, EVP_PKEY **pkey)
+{
+ int ret = 0, len;
+ BIO *bio = NULL;
+ char *priv_b64 = NULL;
+
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* read the privkey file, create EVP_PKEY */
+ *pkey = PEM_read_PrivateKey(f_privkey, NULL, NULL, NULL);
+ if (!*pkey) {
+ ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* write the privkey to bio */
+ if (!PEM_write_bio_PrivateKey(bio, *pkey, NULL, NULL, 0, NULL, NULL)) {
+ ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* read the privkey from bio */
+ len = BIO_get_mem_data(bio, &priv_b64);
+ if (len <= 0) {
+ ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ *privkey = strndup(priv_b64, len);
+
+cleanup:
+ BIO_free(bio);
+ return ret;
+}
+
+static int
+nc_server_config_new_get_privkey_libssh(const char *privkey_path, char **privkey, EVP_PKEY **pkey)
+{
+ int ret = 0;
+ BIO *bio = NULL;
+ char *priv_b64 = NULL;
+ ssh_key key = NULL;
+
+ ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &key);
+ if (ret) {
+ ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path);
+ goto cleanup;
+ }
+
+ /* exports the key in a format in which OpenSSL can read it */
+ ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &priv_b64);
+ if (ret) {
+ ERR(NULL, "Exporting privkey to base64 failed.");
+ goto cleanup;
+ }
+
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = BIO_write(bio, priv_b64, strlen(priv_b64));
+ if (ret <= 0) {
+ ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* create EVP_PKEY from the b64 */
+ *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ if (!*pkey) {
+ ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error()));
+ ret = 1;
+ goto cleanup;
+ }
+
+ *privkey = strndup(priv_b64, ret);
+
+ /* ok */
+ ret = 0;
+
+cleanup:
+ free(priv_b64);
+ BIO_free(bio);
+ ssh_key_free(key);
+ return ret;
+}
+
+static int
+nc_server_config_new_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format, char **privkey, EVP_PKEY **pkey)
+{
+ int ret = 0;
+ FILE *f_privkey = NULL;
+ char *priv = NULL;
+
f_privkey = fopen(privkey_path, "r");
if (!f_privkey) {
ERR(NULL, "Unable to open file \"%s\".", privkey_path);
@@ -698,101 +936,98 @@
goto cleanup;
}
- if (getline(&header, &len, f_privkey) < 0) {
- ERR(NULL, "Error reading header from file \"%s\".", privkey_path);
- ret = 1;
+ /* read the first line from the privkey to determine it's type */
+ ret = nc_server_config_new_privkey_header_to_format(f_privkey, privkey_path, privkey_format);
+ if (ret) {
+ ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path);
goto cleanup;
}
- rewind(f_privkey);
- if (!strncmp(header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) {
- /* it's PKCS8 (X.509) private key */
- *privkey_type = NC_PRIVKEY_FORMAT_X509;
- ret = nc_server_config_new_get_privkey_openssl(f_privkey, &priv, &priv_pkey);
- } else if (!strncmp(header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) {
- /* it's OpenSSH private key */
- *privkey_type = NC_PRIVKEY_FORMAT_OPENSSH;
- ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
- } else if (!strncmp(header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) {
- /* it's RSA privkey in PKCS1 format */
- *privkey_type = NC_PRIVKEY_FORMAT_RSA;
- ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
- } else if (!strncmp(header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) {
- /* it's EC privkey in SEC1 format */
- *privkey_type = NC_PRIVKEY_FORMAT_EC;
- ret = nc_server_config_new_get_privkey_libssh(privkey_path, &priv, &priv_sshkey);
- } else {
- ERR(NULL, "Private key format not supported.");
+ switch (*privkey_format) {
+ /* fall-through */
+ case NC_PRIVKEY_FORMAT_RSA:
+ case NC_PRIVKEY_FORMAT_EC:
+ case NC_PRIVKEY_FORMAT_X509:
+ /* OpenSSL solely can do this */
+ ret = nc_server_config_new_get_privkey_openssl(privkey_path, f_privkey, &priv, pkey);
+ break;
+ case NC_PRIVKEY_FORMAT_OPENSSH:
+ /* need the help of libssh */
+ ret = nc_server_config_new_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;
+ break;
+ default:
+ ERR(NULL, "Private key format not recognized.");
ret = 1;
- goto cleanup;
+ break;
}
if (ret) {
goto cleanup;
}
- if (pubkey_path) {
- ret = nc_server_config_new_get_pubkey(pubkey_path, &pub, pubkey_type);
- } else {
- ret = nc_server_config_new_privkey_to_pubkey(priv_pkey, priv_sshkey, *privkey_type, &pub, pubkey_type);
- }
- if (ret) {
- ERR(NULL, "Getting public key failed.");
- goto cleanup;
- }
-
- /* strip pubkey's header and footer only if it's generated from pkcs8 key (using OpenSSL),
- * otherwise it's already stripped
- */
- if (!pubkey_path && (*privkey_type == NC_PRIVKEY_FORMAT_X509)) {
- *pubkey = strdup(pub + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER));
- if (!*pubkey) {
- ERRMEM;
- ret = 1;
- goto cleanup;
- }
- (*pubkey)[strlen(*pubkey) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)] = '\0';
- } else {
- *pubkey = strdup(pub);
- if (!*pubkey) {
- ERRMEM;
- ret = 1;
- goto cleanup;
- }
- }
-
/* strip private key's header and footer */
- if (*privkey_type == NC_PRIVKEY_FORMAT_OPENSSH) {
- /* only OpenSSH private keys have different header and footer after processing */
- *privkey = strdup(priv + strlen(NC_OPENSSH_PRIVKEY_HEADER));
- if (!*privkey) {
- ERRMEM;
- ret = 1;
- goto cleanup;
- }
- (*privkey)[strlen(*privkey) - strlen(NC_OPENSSH_PRIVKEY_FOOTER)] = '\0';
- } else {
- /* the rest share the same header and footer */
- *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER));
- if (!*privkey) {
- ERRMEM;
- ret = 1;
- goto cleanup;
- }
- (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
+ *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER));
+ if (!*privkey) {
+ ERRMEM;
+ ret = 1;
+ goto cleanup;
}
+ (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0';
cleanup:
if (f_privkey) {
fclose(f_privkey);
}
- free(header);
- free(pub);
free(priv);
+ return ret;
+}
- ssh_key_free(priv_sshkey);
+int
+nc_server_config_new_get_asym_key_pair(const char *privkey_path, const char *pubkey_path, NC_PUBKEY_FORMAT wanted_pubkey_format,
+ char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey)
+{
+ int ret = 0;
+ EVP_PKEY *priv_pkey = NULL;
+
+ NC_CHECK_ARG_RET(NULL, privkey_path, privkey, privkey_type, pubkey, 1);
+
+ *privkey = NULL;
+ *pubkey = NULL;
+
+ /* get private key base64 and EVP_PKEY */
+ ret = nc_server_config_new_get_privkey(privkey_path, privkey_type, privkey, &priv_pkey);
+ if (ret) {
+ ERR(NULL, "Getting private key from file \"%s\" failed.", privkey_path);
+ goto cleanup;
+ }
+
+ /* get public key, either from file or generate it from the EVP_PKEY */
+ if (!pubkey_path) {
+ if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
+ ret = nc_server_config_new_evp_pkey_to_ssh_pubkey(priv_pkey, pubkey);
+ } else {
+ ret = nc_server_config_new_evp_pkey_to_spki_pubkey(priv_pkey, pubkey);
+ }
+ } else {
+ if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) {
+ ret = nc_server_config_new_get_ssh_pubkey_file(pubkey_path, pubkey);
+ } else {
+ ret = nc_server_config_new_get_spki_pubkey_file(pubkey_path, pubkey);
+ }
+ }
+ if (ret) {
+ if (pubkey_path) {
+ ERR(NULL, "Getting public key from file \"%s\" failed.", pubkey_path);
+ } else {
+ ERR(NULL, "Generating public key from private key failed.");
+ }
+ goto cleanup;
+ }
+
+cleanup:
EVP_PKEY_free(priv_pkey);
-
return ret;
}
@@ -911,29 +1146,36 @@
}
API int
-nc_server_config_new_keystore_asym_key(const struct ly_ctx *ctx, const char *asym_key_name, const char *privkey_path,
- const char *pubkey_path, struct lyd_node **config)
+nc_server_config_new_keystore_asym_key(const struct ly_ctx *ctx, NC_TRANSPORT_IMPL ti, const char *asym_key_name,
+ const char *privkey_path, const char *pubkey_path, struct lyd_node **config)
{
int ret = 0;
char *privkey = NULL, *pubkey = NULL;
NC_PRIVKEY_FORMAT privkey_type;
- NC_PUBKEY_FORMAT pubkey_type;
const char *privkey_format, *pubkey_format;
NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, privkey_path, config, 1);
/* get the keys as a string from the given files */
- ret = nc_server_config_new_get_keys(privkey_path, pubkey_path, &privkey, &pubkey, &privkey_type, &pubkey_type);
+ if (ti == NC_TI_LIBSSH) {
+ ret = nc_server_config_new_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey, &privkey_type, &pubkey);
+ } else if (ti == NC_TI_OPENSSL) {
+ ret = nc_server_config_new_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey, &privkey_type, &pubkey);
+ } else {
+ ERR(NULL, "Only SSH and TLS transports can be used to create an asymmetric key pair in the keystore.");
+ ret = 1;
+ goto cleanup;
+ }
if (ret) {
ERR(NULL, "Getting keys from file(s) failed.");
goto cleanup;
}
/* get pubkey format str */
- if (pubkey_type == NC_PUBKEY_FORMAT_X509) {
- pubkey_format = "ietf-crypto-types:public-key-info-format";
- } else {
+ if (ti == NC_TI_LIBSSH) {
pubkey_format = "ietf-crypto-types:ssh-public-key-format";
+ } else {
+ pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
}
/* get privkey identityref value */
@@ -1028,24 +1270,16 @@
{
int ret = 0;
char *pubkey = NULL;
- NC_PUBKEY_FORMAT pubkey_format;
- const char *format;
+ const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
NC_CHECK_ARG_RET(NULL, ctx, pub_bag_name, pubkey_name, pubkey_path, config, 1);
- ret = nc_server_config_new_get_pubkey(pubkey_path, &pubkey, &pubkey_format);
+ ret = nc_server_config_new_get_ssh_pubkey_file(pubkey_path, &pubkey);
if (ret) {
goto cleanup;
}
- /* pubkey format to str */
- if (pubkey_format == NC_PUBKEY_FORMAT_SSH2) {
- format = "ietf-crypto-types:ssh-public-key-format";
- } else {
- format = "ietf-crypto-types:subject-public-key-info-format";
- }
-
- ret = nc_config_new_create(ctx, config, format, "/ietf-truststore:truststore/public-key-bags/"
+ ret = nc_config_new_create(ctx, config, pubkey_format, "/ietf-truststore:truststore/public-key-bags/"
"public-key-bag[name='%s']/public-key[name='%s']/public-key-format", pub_bag_name, pubkey_name);
if (ret) {
goto cleanup;
diff --git a/src/config_new.h b/src/config_new.h
index 76ae481..3b7401b 100644
--- a/src/config_new.h
+++ b/src/config_new.h
@@ -73,10 +73,10 @@
NC_ALG_MAC
} NC_ALG_TYPE;
-int nc_server_config_new_get_keys(const char *privkey_path, const char *pubkey_path,
- char **privkey, char **pubkey, NC_PRIVKEY_FORMAT *privkey_type, NC_PUBKEY_FORMAT *pubkey_type);
+int nc_server_config_new_get_asym_key_pair(const char *privkey_path, const char *pubkey_path, NC_PUBKEY_FORMAT wanted_pubkey_type,
+ char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey);
-int nc_server_config_new_get_pubkey(const char *pubkey_path, char **pubkey, NC_PUBKEY_FORMAT *pubkey_type);
+int nc_server_config_new_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey);
int nc_server_config_new_read_certificate(const char *cert_path, char **cert);
diff --git a/src/config_new_ssh.c b/src/config_new_ssh.c
index 6c10e2f..db9670e 100644
--- a/src/config_new_ssh.c
+++ b/src/config_new_ssh.c
@@ -41,23 +41,15 @@
int ret = 0;
char *pubkey = NULL, *privkey = NULL;
NC_PRIVKEY_FORMAT privkey_type;
- NC_PUBKEY_FORMAT pubkey_type;
- const char *privkey_format, *pubkey_format;
+ const char *privkey_format, *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
/* get the keys as a string from the given files */
- ret = nc_server_config_new_get_keys(privkey_path, pubkey_path, &privkey, &pubkey, &privkey_type, &pubkey_type);
+ ret = nc_server_config_new_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey, &privkey_type, &pubkey);
if (ret) {
ERR(NULL, "Getting keys from file(s) failed.");
goto cleanup;
}
- /* pubkey format to str */
- if (pubkey_type == NC_PUBKEY_FORMAT_SSH2) {
- pubkey_format = "ietf-crypto-types:ssh-public-key-format";
- } else {
- pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
- }
-
/* get privkey identityref value */
privkey_format = nc_config_new_privkey_format_to_identityref(privkey_type);
if (!privkey_format) {
@@ -372,22 +364,14 @@
{
int ret = 0;
char *pubkey = NULL;
- NC_PUBKEY_FORMAT pubkey_type;
- const char *pubkey_format;
+ const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format";
/* get pubkey data */
- ret = nc_server_config_new_get_pubkey(pubkey_path, &pubkey, &pubkey_type);
+ ret = nc_server_config_new_get_ssh_pubkey_file(pubkey_path, &pubkey);
if (ret) {
goto cleanup;
}
- /* get pubkey format */
- if (pubkey_type == NC_PUBKEY_FORMAT_SSH2) {
- pubkey_format = "ietf-crypto-types:ssh-public-key-format";
- } else {
- pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
- }
-
ret = nc_config_new_create_append(ctx, tree_path, "public-key-format", pubkey_format, config);
if (ret) {
goto cleanup;
diff --git a/src/config_new_tls.c b/src/config_new_tls.c
index 985f176..a094ec6 100644
--- a/src/config_new_tls.c
+++ b/src/config_new_tls.c
@@ -38,11 +38,10 @@
int ret = 0;
char *privkey = NULL, *pubkey = NULL, *cert = NULL;
NC_PRIVKEY_FORMAT privkey_type;
- NC_PUBKEY_FORMAT pubkey_type;
- const char *privkey_format, *pubkey_format;
+ const char *privkey_format, *pubkey_format = "ietf-crypto-types:subject-public-key-info-format";
/* get the keys as a string from the given files */
- ret = nc_server_config_new_get_keys(privkey_path, pubkey_path, &privkey, &pubkey, &privkey_type, &pubkey_type);
+ ret = nc_server_config_new_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey, &privkey_type, &pubkey);
if (ret) {
ERR(NULL, "Getting keys from file(s) failed.");
goto cleanup;
@@ -55,13 +54,6 @@
goto cleanup;
}
- /* get pubkey format str */
- if (pubkey_type == NC_PUBKEY_FORMAT_X509) {
- pubkey_format = "ietf-crypto-types:public-key-info-format";
- } else {
- pubkey_format = "ietf-crypto-types:ssh-public-key-format";
- }
-
/* get privkey identityref value */
privkey_format = nc_config_new_privkey_format_to_identityref(privkey_type);
if (!privkey_format) {
diff --git a/src/server_config.c b/src/server_config.c
index 7ffaad8..b6f693e 100644
--- a/src/server_config.c
+++ b/src/server_config.c
@@ -26,6 +26,9 @@
#include <libyang/libyang.h>
#ifdef NC_ENABLED_SSH_TLS
+#include <openssl/err.h>
+#include <openssl/evp.h> // EVP_PKEY_free
+#include <openssl/x509.h> // d2i_PUBKEY
#include <openssl/x509_vfy.h> // X509_STORE_free
#endif
@@ -525,7 +528,7 @@
return NC_PRIVKEY_FORMAT_RSA;
} else if (!strcmp(format, "ec-private-key-format")) {
return NC_PRIVKEY_FORMAT_EC;
- } else if (!strcmp(format, "subject-private-key-info-format")) {
+ } else if (!strcmp(format, "private-key-info-format")) {
return NC_PRIVKEY_FORMAT_X509;
} else if (!strcmp(format, "openssh-private-key-format")) {
return NC_PRIVKEY_FORMAT_OPENSSH;
@@ -2006,7 +2009,7 @@
format = ((struct lyd_node_term *)node)->value.ident->name;
if (!strcmp(format, "ssh-public-key-format")) {
- pubkey_type = NC_PUBKEY_FORMAT_SSH2;
+ pubkey_type = NC_PUBKEY_FORMAT_SSH;
} else if (!strcmp(format, "subject-public-key-info-format")) {
pubkey_type = NC_PUBKEY_FORMAT_X509;
} else {
@@ -2114,6 +2117,41 @@
}
static int
+nc_server_config_is_pk_subject_public_key_info(const char *b64)
+{
+ int ret = 0;
+ long len;
+ char *bin = NULL, *tmp;
+ EVP_PKEY *pkey = NULL;
+
+ /* base64 2 binary */
+ len = nc_base64_to_bin(b64, &bin);
+ if (len == -1) {
+ ERR(NULL, "Decoding base64 public key to binary failed.");
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* for deallocation later */
+ tmp = bin;
+
+ /* try to create EVP_PKEY from the supposed SubjectPublicKeyInfo binary data */
+ pkey = d2i_PUBKEY(NULL, (const unsigned char **)&tmp, len);
+ if (pkey) {
+ /* success, it's most likely SubjectPublicKeyInfo pubkey */
+ ret = 1;
+ } else {
+ /* fail, it's most likely not SubjectPublicKeyInfo pubkey */
+ ret = 0;
+ }
+
+cleanup:
+ EVP_PKEY_free(pkey);
+ free(bin);
+ return ret;
+}
+
+static int
nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
@@ -2137,6 +2175,13 @@
goto cleanup;
}
+ /* the public key must not be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
+ if (nc_server_config_is_pk_subject_public_key_info(lyd_get_value(node))) {
+ ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH hostkey is forbidden!");
+ ret = 1;
+ goto cleanup;
+ }
+
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
/* set to local */
hostkey->store = NC_STORE_LOCAL;
@@ -2176,6 +2221,13 @@
goto cleanup;
}
+ /* the public key must not be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
+ if (nc_server_config_is_pk_subject_public_key_info(lyd_get_value(node))) {
+ ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH user's key is forbidden!");
+ ret = 1;
+ goto cleanup;
+ }
+
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
ret = nc_server_config_replace_auth_key_public_key_leaf(node, pubkey);
if (ret) {
@@ -2191,6 +2243,13 @@
goto cleanup;
}
+ /* the public key must be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */
+ if (!nc_server_config_is_pk_subject_public_key_info(lyd_get_value(node))) {
+ ERR(NULL, "TLS server certificate's Public Key must be in the SubjectPublicKeyInfo format!");
+ ret = 1;
+ goto cleanup;
+ }
+
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
/* set to local */
opts->store = NC_STORE_LOCAL;
@@ -2364,12 +2423,18 @@
}
if (i == ks->asym_key_count) {
- ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
+ ERR(NULL, "Keystore entry \"%s\" not found.", lyd_get_value(node));
return 1;
}
hostkey->ks_ref = &ks->asym_keys[i];
+ /* check if the referenced public key is SubjectPublicKeyInfo */
+ if (nc_server_config_is_pk_subject_public_key_info(hostkey->ks_ref->pubkey_data)) {
+ ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH hostkey is forbidden!");
+ return 1;
+ }
+
return 0;
}
@@ -2530,7 +2595,7 @@
}
static int
-nc_server_config_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
+nc_server_config_ssh_replace_truststore_reference(const struct lyd_node *node, struct nc_client_auth *client_auth)
{
uint16_t i;
struct nc_truststore *ts = &server_opts.truststore;
@@ -2549,6 +2614,14 @@
client_auth->ts_ref = &ts->pub_bags[i];
+ /* check if any of the referenced public keys is SubjectPublicKeyInfo */
+ for (i = 0; i < client_auth->ts_ref->pubkey_count; i++) {
+ if (nc_server_config_is_pk_subject_public_key_info(client_auth->ts_ref->pubkeys[i].data)) {
+ ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH user's key is forbidden!");
+ return 1;
+ }
+ }
+
return 0;
}
@@ -2601,7 +2674,7 @@
/* set to truststore */
auth_client->store = NC_STORE_TRUSTSTORE;
- ret = nc_server_config_replace_truststore_reference(node, auth_client);
+ ret = nc_server_config_ssh_replace_truststore_reference(node, auth_client);
if (ret) {
goto cleanup;
}
diff --git a/src/server_config.h b/src/server_config.h
index ec48fb2..bf12757 100644
--- a/src/server_config.h
+++ b/src/server_config.h
@@ -138,6 +138,7 @@
* @brief Creates new YANG data nodes for an asymmetric key in the keystore.
*
* @param[in] ctx libyang context.
+ * @param[in] ti Transport in which the key pair will be used. Either SSH or TLS.
* @param[in] asym_key_name Identifier of the asymmetric key pair.
* This identifier is used to reference the key pair.
* @param[in] privkey_path Path to a private key file.
@@ -147,8 +148,8 @@
* Otherwise the new YANG data will be added to the previous data and may override it.
* @return 0 on success, non-zero otherwise.
*/
-int nc_server_config_new_keystore_asym_key(const struct ly_ctx *ctx, const char *asym_key_name, const char *privkey_path,
- const char *pubkey_path, struct lyd_node **config);
+int nc_server_config_new_keystore_asym_key(const struct ly_ctx *ctx, NC_TRANSPORT_IMPL ti, const char *asym_key_name,
+ const char *privkey_path, const char *pubkey_path, struct lyd_node **config);
/**
* @brief Deletes a keystore's asymmetric key from the YANG data.
diff --git a/src/server_config_ks.c b/src/server_config_ks.c
index 9024334..b39f8d8 100644
--- a/src/server_config_ks.c
+++ b/src/server_config_ks.c
@@ -261,7 +261,7 @@
format = ((struct lyd_node_term *)node)->value.ident->name;
if (!strcmp(format, "ssh-public-key-format")) {
- key->pubkey_type = NC_PUBKEY_FORMAT_SSH2;
+ key->pubkey_type = NC_PUBKEY_FORMAT_SSH;
} else if (!strcmp(format, "subject-public-key-info-format")) {
key->pubkey_type = NC_PUBKEY_FORMAT_X509;
} else {
diff --git a/src/server_config_ts.c b/src/server_config_ts.c
index e32ef99..11fec83 100644
--- a/src/server_config_ts.c
+++ b/src/server_config_ts.c
@@ -544,7 +544,7 @@
format = ((struct lyd_node_term *)node)->value.ident->name;
if (!strcmp(format, "ssh-public-key-format")) {
- pkey->type = NC_PUBKEY_FORMAT_SSH2;
+ pkey->type = NC_PUBKEY_FORMAT_SSH;
} else if (!strcmp(format, "subject-public-key-info-format")) {
pkey->type = NC_PUBKEY_FORMAT_X509;
} else {
diff --git a/src/session.c b/src/session.c
index 3851a1d..ad96fc5 100644
--- a/src/session.c
+++ b/src/session.c
@@ -36,6 +36,7 @@
#ifdef NC_ENABLED_SSH_TLS
#include <libssh/libssh.h>
+#include <openssl/bio.h>
#include <openssl/conf.h>
#include <openssl/err.h>
@@ -120,6 +121,68 @@
}
}
+int
+nc_base64_to_bin(const char *base64, char **bin)
+{
+ BIO *bio, *bio64;
+ size_t used = 0, size = 0, r = 0;
+ void *tmp = NULL;
+ int nl_count, i, remainder;
+ char *b64;
+
+ /* insert new lines into the base64 string, so BIO_read works correctly */
+ nl_count = strlen(base64) / 64;
+ remainder = strlen(base64) - 64 * nl_count;
+ b64 = calloc(strlen(base64) + nl_count + 1, 1);
+ if (!b64) {
+ ERRMEM;
+ return -1;
+ }
+
+ for (i = 0; i < nl_count; i++) {
+ /* copy 64 bytes and add a NL */
+ strncpy(b64 + i * 65, base64 + i * 64, 64);
+ b64[i * 65 + 64] = '\n';
+ }
+
+ /* copy the rest */
+ strncpy(b64 + i * 65, base64 + i * 64, remainder);
+
+ bio64 = BIO_new(BIO_f_base64());
+ if (!bio64) {
+ ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+
+ bio = BIO_new_mem_buf(b64, strlen(b64));
+ if (!bio) {
+ ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+
+ BIO_push(bio64, bio);
+
+ /* store the decoded base64 in bin */
+ *bin = NULL;
+ do {
+ size += 64;
+
+ tmp = realloc(*bin, size);
+ if (!tmp) {
+ free(*bin);
+ return -1;
+ }
+ *bin = tmp;
+
+ r = BIO_read(bio64, *bin + used, 64);
+ used += r;
+ } while (r == 64);
+
+ free(b64);
+ BIO_free_all(bio64);
+ return size;
+}
+
#endif /* NC_ENABLED_SSH_TLS */
int
diff --git a/src/session_p.h b/src/session_p.h
index d6ca52d..1b12733 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -63,8 +63,8 @@
* Enumeration of SSH public key formats.
*/
typedef enum {
- NC_PUBKEY_FORMAT_SSH2, /**< begins with BEGIN SSH2 PUBLICKEY, see RFC 4716 */
- NC_PUBKEY_FORMAT_X509 /**< begins with BEGIN PUBLICKEY, see RFC 5280 sec. 4.1.2.7 */
+ NC_PUBKEY_FORMAT_SSH, /**< see RFC 4253, section 6.6 */
+ NC_PUBKEY_FORMAT_X509 /**< see RFC 5280 sec. 4.1.2.7 */
} NC_PUBKEY_FORMAT;
/**
@@ -745,6 +745,15 @@
*/
const char *nc_privkey_format_to_str(NC_PRIVKEY_FORMAT format);
+/**
+ * @brief Decodes base64 to binary.
+ *
+ * @param[in] base64 Base64 string.
+ * @param[out] bin Binary result, memory managed by the caller.
+ * @return Length of the binary data on success, -1 on error.
+ */
+int nc_base64_to_bin(const char *base64, char **bin);
+
#endif /* NC_ENABLED_SSH_TLS */
void *nc_realloc(void *ptr, size_t size);